library(dplyr)
library(tidyr)
library(ggplot2)
library(naniar)
library(lubridate)
library(zoo)
library(stringr)
library(emmeans)
library(car)
library(rstatix)
library(ggpubr)
library(lmtest)
library(readr)
library(sf)
library(viridis)
library(gridExtra)
Aqui vamos a estudiar los contaminantes individualmente.
Primero de todo, vamos a cargar la base de datos.
df <- read.csv("~/Documents/Drive/UPV/Segundo/2ºCuatri/Proyecto/base_para_obj1.csv", stringsAsFactors = FALSE)
df <- df %>%
mutate(FechaHora = ymd_hms(paste(Fecha, Hora, "00", "00", sep = ":"), tz = "UTC"))
Ahora que ya sabemos la distribución de los datos, es decir en que años y que estaciones se miden (documento de prep_csv_obj1), vamos a empezar el analisis de los contaminantes, exclusivamente. Me refiero a que vamos a estudiar las siguientes variables:
NO2NONOxO3SO2PM10PM2.5COAhora, vamos a ir contaminante a contaminante analizandolos.
Primero de todo vamos a ver la distribución de los datos de PM10.
hist(df$PM10, breaks = 30, col = "skyblue", main = "Histograma de PM10", xlab = "PM10 (µg/m³)")
Lo que más llama la atención es que esta muy sesgado hacia la derecha:
La gran mayoría de los valores se agrupan en el rango bajo, mientras que hay una “cola” alargada que llega hasta valores muy altos. Esto indica que, aunque normalmente las concentraciones son bajas, existen episodios puntuales con picos muy elevados.
Esto podría deberse a la presencia de datos atípicos o outliers, que son valores que se desvían significativamente de la tendencia general de los datos. Para confirmar esto, vamos a calcular algunos estadísticos descriptivos.
summary(df$PM10)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 1.00 9.00 16.00 20.05 25.00 1675.00 266941
Podemos observar que el valor máximo de la variable es 1675, lo cual es muchísimo más alto de lo normal. Vamos a ver de donde provienen esos valores tan elevados. Para ello, vamos a ver la distribución del contaminante a lo largo del tiempo.
df$FechaHora <- as.POSIXct(df$FechaHora, format = "%Y-%m-%d %H:%M:%S")
ggplot(df, aes(x = FechaHora, y = PM10, group = 1)) +
geom_line(color = "steelblue") +
scale_x_datetime(
date_breaks = "1 year", # un tick por año
date_labels = "%Y" # etiqueta con solo el año
) +
labs(
title = "Evolución temporal de PM10",
x = "Año",
y = expression(PM[10]~"(µg/m"^3*")")
) +
theme_minimal()
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_line()`).
En la gráfica de enicma podemos observar que los valores se disparan a finales de 2024, vamos a ver las fechas exactas. Esto lo vamos a hacer mostrando la fecha en la que el valor de PM10 supera los 500 por primera vez, que solo se superan a finales de 2024.
min(df$FechaHora[df$PM10 > 500], na.rm = TRUE)
## [1] "2024-11-17 18:00:00 UTC"
Esta es la fecha en la que se supera por primera vez el valor de 500 (según la OMS es muy peligroso para la salud), y vemos que es el 17 de noviembre de 2024. La DANA en Valencia empezó el 29 de octubre de 2024, por lo que tiene sentido que el pico se produzca en Noviembre y Diciembre.
Además, dado que PM10 son partículas suspendidas en el aire, es normal que en episodios de lluvias intensas se produzcan picos de contaminación.
Para estudiar la distribución de la variable vamos a aislar los valores de la época de la DANA, ya que es un episodio excepcional. Nos interesa hacer esto para ver la distribución de la variable en condiciones más normales.
df_sin_DANA <- subset(df, FechaHora < as.POSIXct("2024-11-17 18:00:00"))
ggplot(df_sin_DANA, aes(x = PM10)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de PM10",
x = expression(PM[10]~"(µg/"*m^3*")"),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 261310 rows containing non-finite outside the scale range
## (`stat_bin()`).
Ahora, se muestra una distribución un poco más normal, aunque sigue habiendo una asimétria a la derecha. Esto es normal, ya que los contaminantes suelen tener más o menos los mismos valores excepto momentos puntuales en los que se disparan.
Vamos a aplicar una transformación logarítmica a la variable para ver si conseguimos una distribución más normal. Esto es algo que se suele hacer en estadística cuando tenemos datos sesgados.
df_sin_DANA$log_PM10 <- log(df_sin_DANA$PM10 + 1) # Se suma 1 para evitar log(0)
ggplot(df_sin_DANA, aes(x = log_PM10)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de log(PM10)",
x = expression(log(PM[10]~"(µg/"*m^3*")")),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 261310 rows containing non-finite outside the scale range
## (`stat_bin()`).
Al aplicar la transformación logarítmica a la variable, la distribución se vuelve más simétrica y menos sesgada. Esto es un indicativo de que la transformación ha funcionado y que los datos son más normales.
Ahora vamos a estudiar la distribución en la época de la DANA, para ver si hay diferencias significativas entre la época de la DANA y la época normal. Primero de todo vamos a ver que estaciones son las que tienen esos valores tan altos.
df %>%
filter(PM10 > 500) %>%
distinct(Origen)
## Origen
## 1 Benetusser UM
## 2 Catarroja UM
## 3 Paiporta UM
## 4 Massanassa_UM
Vamos a ver cuando empezaron a medir estas estaciones, ya que podrian haber empezado a medir en la DANA, porque como vimo los valores altos se detectaron unas semanas despues de la DANA.
df %>%
filter(Origen == "Massanassa_UM") %>%
summarise(primera_FechaHora = min(FechaHora, na.rm = TRUE))
## primera_FechaHora
## 1 2024-11-13 11:00:00
df %>%
filter(Origen == "Catarroja UM") %>%
summarise(primera_FechaHora = min(FechaHora, na.rm = TRUE))
## primera_FechaHora
## 1 2024-11-22 12:00:00
df %>%
filter(Origen == "Paiporta UM") %>%
summarise(primera_FechaHora = min(FechaHora, na.rm = TRUE))
## primera_FechaHora
## 1 2024-11-25 17:00:00
df %>%
filter(Origen == "Benetusser UM") %>%
summarise(primera_FechaHora = min(FechaHora, na.rm = TRUE))
## primera_FechaHora
## 1 2024-12-20 11:00:00
Observamos que estas estaciones empezaron a medir en la DANA. Además, hemos observado que estos sensores se pusieron explicitamente para medir la DANA, y su ubicación es en el pueblo en si.
df_DANA <- subset(df, FechaHora > as.POSIXct("2024-11-17 18:00:00"))
ggplot(df_DANA, aes(x = PM10)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de PM10 en la DANA",
x = expression(PM[10]~"(µg/"*m^3*")"),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 5626 rows containing non-finite outside the scale range
## (`stat_bin()`).
Vemos que la distribución en la época de la DANA es parecida a la distribución de la variable en condiciones normales, es decir, parece ser que la variable se comporta igual solo que con valores más altos debidos a la DANA.
Vamos a aplicar la transformación logarítmica a la variable para ver si conseguimos una distribución más normal.
df_DANA$log_PM10 <- log(df_DANA$PM10 + 1)
ggplot(df_DANA, aes(x = log_PM10)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de log(PM10) en la DANA",
x = expression(log(PM[10]~"(µg/"*m^3*")")),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 5626 rows containing non-finite outside the scale range
## (`stat_bin()`).
Al igual que antes, al aplicar la transformación logarítmica a la variable, la distribución se vuelve más simétrica y menos sesgada. Esto es un indicativo de que la transformación ha funcionado y que los datos son más normales.
Ahora vamos a ver la distribucion de todos los datos (DANA + no_DANA), aplicando la transformación logarítmica.
df$log_PM10 <- log(df$PM10 + 1)
ggplot(df, aes(x = log_PM10)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de log(PM10)",
x = expression(log(PM[10]~"(µg/"*m^3*")")),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 266941 rows containing non-finite outside the scale range
## (`stat_bin()`).
Vemos que la distribución de PM10 al aplicar la transformación
logarítmica es bastante normal (con un poco de sesgo a la derecha)
incluyendo la DANA, excluyendo la DANA o incluso solo durante la DANA.
Esto nos indica que el contaminante se comporta más o menos simpre
igual, aunque en la DANA los valores son más altos.
Vamos a ver la distribución del contaminante por zonas
df %>%
filter(!is.na(PM10)) %>%
ggplot(aes(x = PM10)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de PM10 por estación",
x = "PM10 (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
Vemos que en la mayoría de las estaciones la distribución de los calores es muy similar. Resalta un poco más el hecho de que hay algunas con unas colas un poco más alargadas, estas estaciones son las afectadas por la DANA. Lo cual tiene sentido que sus colas sean más largas ya que hubo una temporada con valores extremadamente altos. Vamos a ver la distribucion logaritmica.
df %>%
filter(!is.na(log_PM10)) %>%
ggplot(aes(x = log_PM10)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de log(PM10) por estación",
x = "log(PM10) (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
En estas distribuciones, parece ser que si existen diferencias significativas en los valores de PM10 dependiendo de la zona de medicion. Esto lo veremos más adelante.
Vamos a ver gráficamente las medias de PM10 para cada estación para asi ver si varia mucho los valores.
no_tomar <- c("València - Bulevard Sud",
"Paterna - CEAM",
"València - Nazaret Met-2",
"València - Vivers",
"València-Conselleria Meteo")
estaciones_pm10 <- setdiff(unique(df$Origen), no_tomar)
df_pm10 <- df %>%
filter(Origen %in% estaciones_pm10)
# 2) Calcular la media de PM10 por estación (ignorando los NA)
media_por_estacion <- df_pm10 %>%
group_by(Origen) %>%
summarise(
media_PM10 = mean(PM10, na.rm = TRUE),
n_obs = sum(!is.na(PM10))
) %>%
ungroup() %>%
arrange(desc(media_PM10)) %>%
mutate(Origen = factor(Origen, levels = Origen))
# 3) Gráfico de barras
ggplot(media_por_estacion, aes(x = Origen, y = media_PM10)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(media_PM10, 1)),
vjust = -0.5, size = 3) +
labs(
title = "Media de PM10 por estación",
subtitle = "Solo estaciones que miden PM10",
x = "Estación",
y = expression(Media~PM[10]~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(media_por_estacion$Origen, "=", media_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Observamos valores promedio muy altos en las primeras 4 estaciones:
Estas estaciones son las más afectadas por la dana, y además ya vimos que solo tienen valores en 2024. Por lo que sus medias alteran el resultado ya que son casos excepcionales. Vamos a hacer lo mismo pero omitiendo la DANA.
estaciones_DANA <- c("Massanassa_UM", "Benetusser UM", "Catarroja UM", "Paiporta UM", "Sedavi UM")
estaciones_pm10_sin_DANA <- setdiff(estaciones_pm10, estaciones_DANA)
df_pm10 <- df %>%
filter(Origen %in% estaciones_pm10_sin_DANA)
# 2) Calcular la media de PM10 por estación (ignorando los NA)
media_por_estacion <- df_pm10 %>%
group_by(Origen) %>%
summarise(
media_PM10 = mean(PM10, na.rm = TRUE),
n_obs = sum(!is.na(PM10))
) %>%
ungroup() %>%
arrange(desc(media_PM10)) %>%
mutate(Origen = factor(Origen, levels = Origen))
# 3) Gráfico de barras
ggplot(media_por_estacion, aes(x = Origen, y = media_PM10)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(media_PM10, 1)),
vjust = -0.5, size = 3) +
labs(
title = "Media de PM10 por estación sin la DANA",
subtitle = "Solo estaciones que miden PM10",
x = "Estación",
y = expression(Media~PM[10]~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(media_por_estacion$Origen, "=", media_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Observamos que si que varía la media de PM10 dependiendo de su estación de medición, siendo en Valencia Olivereta el más alto. Vamos a hacer lo mismo pero para la mediana.
df_pm10 <- df %>%
filter(Origen %in% estaciones_pm10_sin_DANA)
# 2) Calcular la mediana de PM10 por estación (ignorando los NA)
mediana_por_estacion <- df_pm10 %>%
group_by(Origen) %>%
summarise(
mediana_PM10 = median(PM10, na.rm = TRUE),
n_obs = sum(!is.na(PM10))
) %>%
ungroup() %>%
arrange(desc(mediana_PM10)) %>%
mutate(Origen = factor(Origen, levels = Origen))
# 3) Gráfico de barras
ggplot(mediana_por_estacion, aes(x = Origen, y = mediana_PM10)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(mediana_PM10, 1)),
vjust = -0.5, size = 3) +
labs(
title = "Mediana de PM10 por estación sin la DANA",
subtitle = "Solo estaciones que miden PM10",
x = "Estación",
y = expression(Mediana~PM[10]~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(mediana_por_estacion$Origen, "=", mediana_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Ahora observamos casi lo mismo que con la media, solo que algunas estaciones como València Port llit antic Túria supera a Quart de Poblet, cuando con la media no pasaba.
Ahora vamos a realizar lo mismo pero para la desviación típica.
df_pm10 <- df %>%
filter(Origen %in% estaciones_pm10_sin_DANA)
sd_por_estacion <- df_pm10 %>%
group_by(Origen) %>%
summarise(
sd_PM10 = sd(PM10, na.rm = TRUE),
n_obs = sum(!is.na(PM10))
) %>%
ungroup() %>%
arrange(desc(sd_PM10)) %>%
mutate(Origen = factor(Origen, levels = Origen))
ggplot(sd_por_estacion, aes(x = Origen, y = sd_PM10)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(sd_PM10, 2)),
vjust = -0.5, size = 3) +
labs(
title = "Desviación típica de PM10 por estación",
subtitle = "Solo estaciones que miden PM10",
x = "Estación",
y = expression(SD~PM10~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(sd_por_estacion$Origen, "=", sd_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Podemos observar que Quart de Poblet es la estación con la mayor desviación típica, lo cual indica que allí fluctuan mucho de una hra a otra. Estas fluctuaciones, pueden deberse a varios factores como por ejemplo el tráfico y la temperatura. Luego observamos que por ejemplo, València Politècnic tiene la menor desviación estandar por lo que sus valores tienden a ser más estacionarios.
Ahora vamos a ver los maximos y minimos medidos en cada estación.
df_pm10 <- df %>%
filter(Origen %in% estaciones_pm10_sin_DANA)
# 2) Calcular el valor mínimo y máximo de PM10 por estación
resumen_pm10 <- df_pm10 %>%
group_by(Origen) %>%
summarise(
min_PM10 = min(PM10, na.rm = TRUE),
max_PM10 = max(PM10, na.rm = TRUE)
) %>%
ungroup() %>%
arrange(Origen)
print(resumen_pm10)
## # A tibble: 10 × 3
## Origen min_PM10 max_PM10
## <chr> <int> <int>
## 1 Quart de Poblet 1 467
## 2 Torrent-El Vedat 1 257
## 3 València - Av. França 1 382
## 4 València - Centre 1 346
## 5 València - Molí del Sol 1 262
## 6 València - Pista de Silla 1 332
## 7 València - Politècnic 1 189
## 8 València Olivereta 2 460
## 9 València Port Moll Trans. Ponent 1 354
## 10 València Port llit antic Túria 1 284
ggplot(resumen_pm10, aes(x = Origen)) +
geom_linerange(aes(ymin = min_PM10, ymax = max_PM10), size = 1.5) +
geom_point(aes(y = min_PM10), shape = 21, fill = "white", size = 3) +
geom_point(aes(y = max_PM10), shape = 21, fill = "black", size = 3) +
coord_flip() +
labs(
title = "Rango de valores de PM10 por estación",
x = "Estación",
y = expression(PM[10]~"(µg/m³)")
) +
theme_minimal(base_size = 12)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
Con el gráfico y la tabla de arriba podemos observar el rango de valores de cada estación. Podemos ver que Quart de Poblet y València Olivereta tienen rangos más altos, mientras que en València Politècnic los valores de PM10 varían menos. Esto ayuda a comprender los gráficos anterirores ya que lso que tiene los rangos más grandes son los que tienen medias y medianas más altas.
Ahora, vamos a estudiar si hay diferencias significativas en los valores de PM10 dependiendo de la estación de medición. Para ello, podríamos aplicar un ANOVA, pero dado que son series temporales, vamos a coger una muestra de 200 valores para mejorar las hipotesis del ANOVA.
set.seed(123) # Para reproducibilidad
df_pm10 = df_sin_DANA%>%
filter(FechaHora >= "2023-01-01" & FechaHora < "2024-01-01") %>%
filter(hour(FechaHora) == 12) %>%
filter(wday(FechaHora) == 2) %>%
sample_n(200)
df_pm10$log_PM10 = log(df_pm10$PM10)
fit <- aov(PM10 ~ Origen, data = df_pm10)
summary(fit)
## Df Sum Sq Mean Sq F value Pr(>F)
## Origen 9 8262 918.0 3.72 0.000397 ***
## Residuals 115 28380 246.8
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 75 observations deleted due to missingness
El valor de F y el p-value indican que podemos rechazar, con confianza, la hipotesis nula. Es decir, hay diferencias significativas entre las medias de los grupos, en este caso las estaciones de medición.
Antes de ver a que niveles existen diferenias significativas o no, vamos a comprobar las hipotesis del ANOVA. Primero vamos a comprobar la independencia de los residuos, lo haremos con el test de Durbin-Watson.
dw <- dwtest(fit)
print(dw)
##
## Durbin-Watson test
##
## data: fit
## DW = 2.1049, p-value = 0.7044
## alternative hypothesis: true autocorrelation is greater than 0
El hecho de que el valor DW sea mayor que 2, indica ausencia de autocorrelación de primer grado. Además, el p-value es mucho mayor que 0.05, por lo que no hay información suficiente para rechazar la hipotesis nula de no haber autocorrelación positiva. Debido a eso se cumple la condición de independencia.
Ahora vamos a comprobar la homogeniedad.
leveneTest(fit)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 9 1.1258 0.3504
## 115
Como el p-value es mayor que alpha, no podemos rechazar la igualdad de varianzas y por ello cumple la condición de homogeniedad del ANOVA.
Vamos a visualizar unos gráficos que demuestren lo indicado.
par(mfrow = c(1, 1))
plot(fit)
Aparte de confirmar lo anterior, en el QQ-plot podemos comprobar la normalidad ya que los puntos siguen la recta bastante consitentemente. Además, el gráfico de Cook’s distance confirma que no hay valores influyentes.
Ahora vamos a ver donde están esas diferencias. Para ello, vamos a aplicar un test de Tukey, el cual nos dará las diferencias significativas entre los diferentes pares de estaciones.
tukey_pm10 <- TukeyHSD(fit)
res <- as.data.frame(tukey_pm10$Origen)
res_ns <- subset(res, `p adj` <= 0.05)
print(res_ns)
## diff lwr upr
## València - Molí del Sol-Quart de Poblet -22.07143 -40.90820 -3.234659
## València - Politècnic-Quart de Poblet -22.93750 -41.15515 -4.719852
## València Port Moll Trans. Ponent-Quart de Poblet -20.78571 -39.62248 -1.948945
## p adj
## València - Molí del Sol-Quart de Poblet 0.009072602
## València - Politècnic-Quart de Poblet 0.003422488
## València Port Moll Trans. Ponent-Quart de Poblet 0.018524485
En la tabla solo se muestran las combinaciones que presentan diferencias significativas entre ellas (p-value <= 0.05).
Esto es sorprendente, ya que estas zonas no son las que tienen valores más altos ni más bajos, de hecho, en la graficas de media y mediana tienen valores similares.
Esto indica que en estas zonas los valores de PM10 se ven afectados por su ubicación, esto podría deberse a muchos factores. Por ejemplo, a la densidad de tráfico en esas zonas, ya que unas estan más en el centro de la ciudad que otras. O tambien podría deberse a la meteorología, ya que puede variar. Todo esto lo estudiaremos más adelante.
En resumen, los valores de PM10 no se ven afectados por la zona en la gran mayoría de casos. Por lo que la estación de medición no parece ser muy influyente.
Vamos a ver como se comporta el contaminante a lo largo del tiempo. Para ello, vamos a hacer un gráfico con la evolución de la variable a lo largo del tiempo.
df_sin_DANA$FechaHora <- as.POSIXct(df_sin_DANA$FechaHora, format = "%Y-%m-%d %H:%M:%S")
ggplot(df_sin_DANA, aes(x = FechaHora, y = PM10, group = 1)) +
geom_line(color = "steelblue") +
scale_x_datetime(
date_breaks = "1 year",
date_labels = "%Y"
) +
labs(
title = "Evolución temporal de PM10",
x = "Año",
y = expression(PM[10]~"(µg/m"^3*")")
) +
theme_minimal()
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_line()`).
En el gráfico, parece haber un ligero comportamiento estacional, es decir, parece que hay subidas y bajadas en los valores de PM10 a lo largo del tiempo. Además, se puede observar que a finales de 2023 y todo 2024 los picos son, en general, más altos.
Ahora vamos a ver un gráfico con la media de PM10 en cada año.
# Cálculo de la media por año
df_anual <- df_sin_DANA %>%
group_by(Año) %>%
summarise(media_PM10 = mean(PM10, na.rm = TRUE)) %>%
mutate(Año = as.numeric(as.character(Año))) %>%
arrange(Año)
# Gráfico con área sombreada y línea en rojo
ggplot(df_anual, aes(x = Año, y = media_PM10)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(
breaks = 2019:2024,
limits = c(2019, 2024)) +
labs(
title = "Yearly evolution of the average PM10 concentration",
subtitle = "Average values from all stations in Valencia",
x = "Year",
y = "Average of PM10 (µg/m³)",
caption = "Source: your air pollution data"
) +
# Tema minimalista con toques de color
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Se puede observar que en 2020 hubo una disminución considerable en los niveles de PM10, probabemente debido a la pandemia de COVID-19 y lsa restricciones que hubo (trafico,..). Luego, los niveles vuelven a aumentar, pero no tanto como en 2019.
Tambien observamos que a partir de 2022, la concentración media comienza a disminuir, lo cual puede deberse a que la UE esta intentando reducir las emisiones de CO2. Las emisiones de CO2 estan relacionadas con PM10 ya que crea un efecto invernadero y no deja que el contaminante se disperse correctamente en la atmosfera.
df_anual_sd <- df_sin_DANA %>%
group_by(Año) %>%
summarise(sd_PM10 = sd(PM10, na.rm = TRUE)) %>%
mutate(Año = as.numeric(as.character(Año))) %>%
arrange(Año)
# Gráfico con área sombreada y línea en rojo
ggplot(df_anual_sd, aes(x = Año, y = sd_PM10)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(
breaks = 2019:2024,
limits = c(2019, 2024)) +
labs(
title = "Yearly evolution of the standard deviation of PM10 concentration",
subtitle = "Average values from all stations in Valencia",
x = "Year",
y = "SD of PM10 (µg/m³)",
caption = "Source: your air pollution data"
) +
# Tema minimalista con toques de color
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Observamos que la desviación típica de PM10 va disminuyendo a partir de 2019 hasta 2021, lo cual significa que habian menos picos de contaminación, probablemente debido a la pandemia. Durante la pandemia no había mucho tráfico por lo que se redujo la contaminación. Y a partir de 2021 la variabilidad del contaminante vuelve a incrementar debido a la vuelta a la normalidad.
Vemos, que sigue la misma estructura que la media, lo cual tiene sentido e indica que la variable PM10 tiene una desviación tipica más o menos constante en terminos de proporciones. Ya que cuando la media de PM10 sube, tambien lo hace la desviacion estandar y lo mismo cuando baja.
Podemos concluir que los valores de PM10 si dependen de el año. Pero a la vez, su varibilidad es proporcionalmente parecida en todos los años.
meses_orden <- c("Enero","Febrero","Marzo","Abril","Mayo","Junio",
"Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre")
df_mensual <- df_sin_DANA %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
group_by(Mes) %>%
summarise(media_PM10 = mean(PM10, na.rm = TRUE)) %>%
arrange(Mes) %>%
mutate(mes_num = as.integer(Mes))
ggplot(df_mensual, aes(x = mes_num, y = media_PM10)) +
geom_area(fill = "#FFEB3B", alpha = 0.4) +
geom_line(color = "#FFC107", size = 1.2) +
geom_point(color = "#FFC107", size = 3) +
scale_x_continuous(
breaks = 1:12,
labels = meses_orden,
limits = c(1, 12)
) +
labs(
title = "Evolución anual del promedio de PM10",
subtitle = "Promedio mensuales desde todas las estaciones en Valencia",
x = "Mes",
y = "Concentración promedio de PM10 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#FFC107"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
En el gráfico, se puede observar que los niveles de PM10 son más altos en los primeros 3 meses del año. Los siguientes meses disminuyen considerablemente, pero en verano vuelven a aumentar un poco y luego vuelven a bajar. No parece haber un patrón claro en los meses, pero al realizar un ANOVA, podemos esperar que los meses causan diferencias significativas en los niveles de PM10. Vamos a comprobarlo.
Ahora vamos a ver como son las desviaciones típicas de PM2.5 para cada mes.
df_mensual_sd <- df_sin_DANA %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
group_by(Mes) %>%
summarise(sd_PM10 = sd(PM10, na.rm = TRUE)) %>%
arrange(Mes) %>%
mutate(mes_num = as.integer(Mes))
ggplot(df_mensual_sd, aes(x = mes_num, y = sd_PM10)) +
geom_area(fill = "#FFEB3B", alpha = 0.4) +
geom_line(color = "#FFC107", size = 1.2) +
geom_point(color = "#FFC107", size = 3) +
scale_x_continuous(
breaks = 1:12,
labels = meses_orden,
limits = c(1, 12)
) +
labs(
title = "Evolución anual de la desviacion típica de PM10",
subtitle = "Desviaciones típicas mensuales desde todas las estaciones en Valencia",
x = "Mes",
y = "Desviación típica de PM10 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#FFC107"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Vemos que la desviación típica varia a la par con la media, es decir cuando una sube la otra también lo hace. Esto nos indica que las desviación típica es bastante constante.
Podemos concluir que las mediciones de PM10 se ven afectadas por el mes en el que se miden, sobre todo en los meses más frios en Valencia.
# 1. Definir el orden correcto de los días
dias_orden <- c("lunes","martes","miércoles","jueves","viernes","sábado","domingo")
df_semanal <- df_sin_DANA %>%
mutate(
Dia_Semana = factor(Dia_Semana, levels = dias_orden, ordered = TRUE)
) %>%
group_by(Dia_Semana) %>%
summarise(media_PM10 = mean(PM10, na.rm = TRUE)) %>%
arrange(Dia_Semana) %>%
mutate(dia_num = as.integer(Dia_Semana))
ggplot(df_semanal, aes(x = dia_num, y = media_PM10)) +
geom_area(fill = "#2196F3", alpha = 0.4) +
geom_line(color = "#1976D2", size = 1.2) +
geom_point(color = "#1976D2", size = 3) +
scale_x_continuous(
breaks = 1:7,
labels = dias_orden,
limits = c(1, 7)
) +
labs(
title = "Evolución semanal de la concentración promedio de PM10",
subtitle = "Promedios semanales desde todas las estaciones en Valencia",
x = "Día de la semana",
y = "Promedio de PM10 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#1976D2"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
En esta ocasión, podemos observar que los niveles de PM10 son más o menos iguales durante la semana excepto los fines de semana, donde los valores parecen disminuir considerablemente. Esto puede deberse a que durante el fin de semana hay menos tráfico y menos actividad industrial, lo que provoca una disminución en los niveles de contaminación.
Vamos a hacer lo mismo para la desviación típica de la variable.
df_semanal_sd <- df_sin_DANA %>%
mutate(
Dia_Semana = factor(Dia_Semana, levels = dias_orden, ordered = TRUE)
) %>%
group_by(Dia_Semana) %>%
summarise(sd_PM10 = sd(PM10, na.rm = TRUE)) %>%
arrange(Dia_Semana) %>%
mutate(dia_num = as.integer(Dia_Semana))
ggplot(df_semanal_sd, aes(x = dia_num, y = sd_PM10)) +
geom_area(fill = "#2196F3", alpha = 0.4) +
geom_line(color = "#1976D2", size = 1.2) +
geom_point(color = "#1976D2", size = 3) +
scale_x_continuous(
breaks = 1:7,
labels = dias_orden,
limits = c(1, 7)
) +
labs(
title = "Evolución semanal de la desviación típica de PM10",
subtitle = "Desviaciones típicas semanales desde todas las estaciones en Valencia",
x = "Día de la semana",
y = "DS de PM10 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#1976D2"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Al igual que en los otros casos, la desviación típica se comporta igual que la media. Pero cabe destacar que el lunes y martes tienen una desviación típica proporcional más baja. Lo que significa que las mediciones en esos días son más constantes.
Por lo que deducimos que los valores de PM10 si se ven afectados por el dia de la semana, probablemente debido al tráfico.
df_hourly <- df_sin_DANA %>%
group_by(Hora) %>%
summarise(media_PM10 = mean(PM10, na.rm = TRUE)) %>%
mutate(Hora = as.numeric(as.character(Hora))) %>%
arrange(Hora)
ggplot(df_hourly, aes(x = Hora, y = media_PM10)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(breaks = 0:23, limits = c(0, 23)) +
labs(
title = "Hourly evolution of the average PM10 concentration",
subtitle = "Average values from all stations in Valencia",
x = "Hour of the day",
y = "Average of PM10 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
En este caso, podemos observar mucha variación en los niveles de PM10 a lo largo del día. Los niveles son bajos por la madrugada, a partir de las 6 sufren un aumento muy notable, alcanzando su máximo a las 8 de la mañana. Luego, a medida que pasa el dia va disminuyendo, hasta las 17 horas, que vuelve a incrementar. Esto podría deberse a la temperatura o el tráfico, lo cual comprobaremos más adelante.
Vamos a hacer lo mismo para la desviación típica.
df_hourly_sd <- df_sin_DANA %>%
group_by(Hora) %>%
summarise(sd_PM10 = sd(PM10, na.rm = TRUE)) %>%
mutate(Hora = as.numeric(as.character(Hora))) %>%
arrange(Hora)
ggplot(df_hourly_sd, aes(x = Hora, y = sd_PM10)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(breaks = 0:23, limits = c(0, 23)) +
labs(
title = "Hourly evolution of the strandard deviation of PM10",
subtitle = "Standard deviation values from all stations in Valencia",
x = "Hour of the day",
y = "SD of PM10 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Como era de esperar, las desviaciones típicas varían junto a la media. Pero a las 11 y 12 las desviaciones típicas proporcionales parecen ser menores. Lo que se puede deber a más constancia en los valores.
Podemos confirmar que la hora del dia influencia bastante en los valores de PM10, probablemente debido al tráfico rodado.
Primero de todo, vamos a ver el porcentaje de faltantes que tenemos en la variable PM10.
sum(is.na(df$PM10)) / nrow(df) * 100
## [1] 39.91659
Tenemos un 40% de faltantes, vamos a ver en que estaciones tenemos más faltantes.
df %>%
filter(is.na(PM10)) %>%
count(Origen) %>%
arrange(desc(n)) %>%
ggplot(aes(x = reorder(Origen, -n), y = n)) +
geom_bar(stat = "identity", fill = "firebrick") +
labs(title = "Número de valores faltantes (NA) en PM10 por estación",
x = "Estación (Origen)",
y = "Número de NAs") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Resalta que hay varias estaciones con un número elevado de faltantes, lo cual se puede deber a que no se mide PM10 en esas estaciones.
resumen_PM10 <- df %>%
select(Origen, PM10) %>%
pivot_longer(
cols = -Origen,
names_to = "Variable",
values_to = "Valor"
) %>%
filter(Variable == "PM10", !is.na(Valor)) %>%
group_by(Variable) %>%
summarise(
num_estaciones = n_distinct(Origen),
lista_estaciones = paste(sort(unique(Origen)), collapse = ", ")
) %>%
ungroup()
print(resumen_PM10)
## # A tibble: 1 × 3
## Variable num_estaciones lista_estaciones
## <chr> <int> <chr>
## 1 PM10 15 Benetusser UM, Catarroja UM, Massanassa_UM, Paiporta …
La variable PM10 se mide en 15 estaciones, las estaciones que no la miden son:
Que son las que más faltantes tienen. Ahora vamos a descartar estas estaciones.
df_plot <- df %>%
filter(Origen %in% estaciones_pm10) %>%
group_by(Origen) %>%
summarise(
n = sum(is.na(PM10)),
total = n(), # total de filas por estación
pct = n / total * 100 # porcentaje de NAs
) %>%
arrange(desc(n))
# 2) Gráfico con porcentaje como etiqueta
ggplot(df_plot, aes(x = reorder(Origen, -n), y = n)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct,1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Número y % de valores faltantes (NA) en PM10 por estación",
x = "Estación (Origen)",
y = "Número de NAs"
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1)
)
En el nuevo gráfico, podemos observar que la mayoría de los faltantes se encuentran en Torrent, donde hay un 43% de faltantes. Esos porcentajes indican la proporción de faltantes en esa estacion.
Ahora vamos a ver cuanto faltantes tenemos en total, es decir, en todas las estaciones.
resumen_total <- df %>%
filter(Origen %in% estaciones_pm10) %>%
summarise(
total_obs = n(),
faltantes = sum(is.na(PM10)),
pct_faltantes = faltantes / total_obs * 100
)
print(resumen_total)
## total_obs faltantes pct_faltantes
## 1 421902 20096 4.763191
Al haber eliminado las estaciones que no miden PM10, nos quedamos con un poco menos de 5% de faltantes. Antes de imputarlos, vamos a ver si hay algún patrón en los faltantes.
Por años:
df_plot <- df %>%
filter(
Origen %in% estaciones_pm10, # opcional: solo ciertas estaciones
is.na(PM10) # quedarnos solo con los NAs
) %>%
count(Año, name = "n_missing") %>% # contar NAs por cada valor de Año
ungroup() %>%
mutate(
total_missing = sum(n_missing), # total de NAs en todos los años
pct_total = n_missing / total_missing * 100 # % sobre el total de NAs
) %>%
arrange(Año)
# 2) Gráfico de barras con porcentaje de faltantes por año
ggplot(df_plot, aes(x = factor(Año), y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de PM10 por año",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese año",
x = "Año",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
La mayoría de los faltantes se concentran en 2023. Ahora vamos a hacer lo mismo pero por meses.
df_plot <- df %>%
filter(Origen %in% estaciones_pm10) %>% # opcional: filtrar solo estaciones_pm10
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
filter(is.na(PM10)) %>% # quedarnos solo con los NAs
count(Mes, name = "n_missing") %>% # contar NAs por mes
ungroup() %>%
mutate(
total_missing = sum(n_missing), # total de NAs
pct_total = n_missing / total_missing * 100 # % de cada mes sobre el total
)
# 2) Gráfico
ggplot(df_plot, aes(x = Mes, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de PM10 por mes",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese mes",
x = "Mes",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
En este gráfico, observamos que la mayoría de los faltantes se encuentran en Septiembre. Ahora lo mismo para días de la semana.
df_plot <- df %>%
filter(
Origen %in% estaciones_pm10, # solo estas estaciones (si aplica)
is.na(PM10) # quedarnos sólo con los NA
) %>%
count(Dia_Semana, name = "n_missing") %>% # contar NAs por día
ungroup() %>%
mutate(
total_missing = sum(n_missing), # total de NAs
pct_total = n_missing / total_missing * 100 # % sobre el total
) %>%
# aseguramos el orden correcto de los días
mutate(Dia_Semana = factor(Dia_Semana, levels = dias_orden)) %>%
arrange(Dia_Semana)
# 2) Gráfico
ggplot(df_plot, aes(x = Dia_Semana, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de PM10 por día de la semana",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese día",
x = "Día de la semana",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle= element_text(hjust = 0.5)
)
Parece ser, que los faltantes se distribuyen uniformemente entre los días de la semana. Ahora vamos a ver por horas del dia.
df_plot <- df %>%
filter(
Origen %in% estaciones_pm10, # filtrar solo las estaciones de interés
is.na(PM10) # quedarnos solo con los NAs
) %>%
count(Hora, name = "n_missing") %>% # contar NAs por hora
ungroup() %>%
mutate(
total_missing = sum(n_missing), # total de NAs
pct_total = n_missing / total_missing * 100 # % sobre el total
) %>%
arrange(Hora)
# 2) Gráfico de barras con porcentaje de faltantes por hora
ggplot(df_plot, aes(x = Hora, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
scale_x_continuous(breaks = 0:23) +
labs(
title = "Porcentaje de valores faltantes de PM10 por hora del día",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en esa hora",
x = "Hora",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle= element_text(hjust = 0.5)
)
Sucede los mismo que con los días de la semana, los faltantes se distribuyen uniformemente entre las horas del día.
Por lo que podemos conluir que parece haber un patron entre los año y meses, ya que ahi se ven diferencias significativas. En el resto de variables, no parece haber un patrón claro.
Para si hay un patrón vamos a ver si un faltante se repite en otros años. Esto lo vamos a hacer siguiendo estos pasos:
-filtrar estaciones que miden la variable - Seleccionar filas donde la variable es NA - Agrupar por combinación de Mes, Dia y Hora - Calcular el numero de años distintos con al menos ese NA y contruimos la lista de años - Solo nos quedamos con los que se repiten en más de un año
df_faltantes_pm10 <- df %>%
filter(Origen %in% estaciones_pm10)
faltantes_recurrentes <- df_faltantes_pm10 %>%
filter(is.na(PM10)) %>%
group_by(Mes, Dia, Hora) %>%
summarise(
n_años = n_distinct(Año),
Años_con_faltante = paste(sort(unique(Año)), collapse = ", "),
.groups = "drop"
) %>%
filter(n_años > 1) %>%
select(Mes, Dia, Hora, Años_con_faltante)
print(faltantes_recurrentes)
## # A tibble: 5,837 × 4
## Mes Dia Hora Años_con_faltante
## <chr> <int> <int> <chr>
## 1 Abril 5 0 2022, 2023
## 2 Abril 5 7 2022, 2023
## 3 Abril 5 8 2022, 2023
## 4 Abril 5 9 2022, 2023
## 5 Abril 7 13 2022, 2023
## 6 Abril 8 16 2023, 2024
## 7 Abril 8 17 2023, 2024
## 8 Abril 8 18 2023, 2024
## 9 Abril 8 19 2023, 2024
## 10 Abril 8 20 2023, 2024
## # ℹ 5,827 more rows
Podemos observar que si parece haber un patrón en los faltantes, ya que hay muchas fechas con el mismo faltante pero en otros años.
df %>%
mutate(missing = is.na(PM10)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c('TRUE' = "red", 'FALSE' = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de PM10",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
df %>%
filter(Origen %in% estaciones_pm10) %>%
mutate(missing = is.na(PM10)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c('TRUE' = "red", 'FALSE' = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de PM10",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
Gracias al gráfico, podemos ver facilmente la distribucion de los faltantes, y es mas sencillo encontrar patrones.
Ahora vamos a hacer lo mismo pero para el contaminante PM2.5. Esperamos que los resultados sean similares a los de PM10, ya que son contaminantes similares y tienen fuentes de emisión similares.
Primero de todo, vamos a ver su distribución.
hist(df$PM2.5, breaks = 30, col = "skyblue", main = "Histograma de PM2.5", xlab = "PM2.5 (µg/m³)")
Como esperábamos, tiene una distribución parecida a la de PM10, solo que menos sesgada a la derecha. Al igual que con PM10, vamos a ver algunos estadisticos descriptivos.
summary(df$PM2.5)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.00 5.00 9.00 11.01 14.00 309.00 266645
Como podemos ver, el valor máximo es 309, el cual esta dentro del rango ‘peligroso’ según la OMS y EPA. En el caso de PM10, los valores tan sumamente altos eran debidos a la DANA, vamos a ver si aquí sucede lo mismo.
ggplot(df, aes(x = FechaHora, y = PM2.5, group = 1)) +
geom_line(color = "steelblue") +
scale_x_datetime(
date_breaks = "1 year",
date_labels = "%Y"
) +
labs(
title = "Evolución temporal de PM2.5",
x = "Año",
y = expression(PM[2.5]~"(µg/m"^3*")")
) +
theme_minimal()
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_line()`).
Podemos observar, que si que hay valores altos a finales de 2024, la fecha de la DANA. Pero a la vez, observamos que hay picos altos en otras fechas, como en 2020 y 2022. Según la OMS, a partir de 125 µg/m3 es insalubre,y a partir de 200 µg/m3 peligroso, por lo que antes de seguir adelante, vamos a analizar estos picos peligrosos.
Primero vamos a ver las fechas de los picos altos, es decir los valores superiores a 200 µg/m3 (excluyendo la DANA).
fechas_valores_exceso <- df_sin_DANA %>%
filter(`PM2.5` > 200) %>%
select(Fecha, Hora, Origen, `PM2.5`) %>%
arrange(Fecha)
print(fechas_valores_exceso)
## Fecha Hora Origen PM2.5
## 1 2020-10-02 12 València Port Moll Trans. Ponent 263
## 2 2020-10-02 13 València Port Moll Trans. Ponent 261
## 3 2022-03-19 20 València - Centre 218
## 4 2022-03-19 23 València - Centre 232
## 5 2022-08-18 0 València Olivereta 308
## 6 2022-08-18 1 València Olivereta 309
## 7 2022-08-18 1 València Port Moll Trans. Ponent 219
## 8 2022-08-18 1 València - Pista de Silla 249
## 9 2022-08-18 1 València Port llit antic Túria 258
## 10 2022-08-18 1 València - Centre 296
Se muestran las fechas, horas y origen de los valores superiores a 200. Lo que más resalta es que para el 2022-08-18 a la 1 de la mañana, hubieron valores altos en 5 estaciones. Lo cual nos hace pensar que sucedió algo.
Ahora vamos a investigar mas a fondo las fechas, para ver si los valores de esos dias son reales o erroneos.
Primero, estación València Port Moll Trans. Ponent y
fecha 2020-10-02.
valores_origen <- df_sin_DANA %>%
filter(
Fecha == as.Date("2020-10-02"),
Origen == "València Port Moll Trans. Ponent"
) %>%
# Convertimos a factor para eje discreto
mutate(Hora = factor(Hora, levels = unique(Hora))) %>%
select(Hora, `PM2.5`) %>%
arrange(as.integer(as.character(Hora)))
ggplot(valores_origen, aes(x = Hora, y = `PM2.5`, group = 1)) +
geom_line(color = "steelblue") +
geom_point(color = "steelblue", size = 2) +
# Eje discreto para factores
scale_x_discrete() +
labs(
title = "PM2.5 por hora el 2020-10-02 (Origen: València Port Moll Trans. Ponent)",
x = "Hora",
y = expression(PM[2.5]~"(µg/m"^3*")")
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
En este caso, parece ser algo justificado ya que incrementa un poco cada hora hasta llegar al maximo, por lo que no parece deberse a un error. Podría deberse a que por ejemplo, duarnate esas hora hubo un mayor trafico de barcos en el puerto.
Segundo, estación València - Centre y fecha
2022-08-18.
valores_origen <- df_sin_DANA %>%
filter(
Fecha == as.Date("2022-08-18"),
Origen == "València - Centre"
) %>%
mutate(Hora = factor(Hora, levels = unique(Hora))) %>%
select(Hora, `PM2.5`) %>%
arrange(as.integer(as.character(Hora)))
ggplot(valores_origen, aes(x = Hora, y = `PM2.5`, group = 1)) +
geom_line(color = "steelblue") +
geom_point(color = "steelblue", size = 2) +
scale_x_discrete() +
labs(
title = "PM2.5 por hora el 2022-08-18 (Origen: València - Centre)",
x = "Hora",
y = expression(PM[2.5]~"(µg/m"^3*")")
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
En este caso, se observa un valor extremadamente alto a la 1 de la madrugada y luego el resto del dia se observan valores normales. Esto parece ser un error.
Tercero, estación València Olivereta y fecha
2022-08-18
valores_origen <- df_sin_DANA %>%
filter(
Fecha == as.Date("2022-08-18"),
Origen == "València Olivereta"
) %>%
mutate(Hora = factor(Hora, levels = unique(Hora))) %>%
select(Hora, `PM2.5`) %>%
arrange(as.integer(as.character(Hora)))
ggplot(valores_origen, aes(x = Hora, y = `PM2.5`, group = 1)) +
geom_line(color = "steelblue") +
geom_point(color = "steelblue", size = 2) +
scale_x_discrete() +
labs(
title = "PM2.5 por hora el 2022-08-18 (Origen: València Olivereta)",
x = "Hora",
y = expression(PM[2.5]~"(µg/m"^3*")")
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
En este caso volvemos a observar lo mismo que antes, lo cual parece un error tambien, pero al ser la misma fecha y misma hora podría no deberse a un error.
Cuarto, estacion València - Pista de Silla y fecha
2022-08-18.
valores_origen <- df_sin_DANA %>%
filter(
Fecha == as.Date("2022-08-18"),
Origen == "València - Pista de Silla"
) %>%
mutate(Hora = factor(Hora, levels = unique(Hora))) %>%
select(Hora, `PM2.5`) %>%
arrange(as.integer(as.character(Hora)))
ggplot(valores_origen, aes(x = Hora, y = `PM2.5`, group = 1)) +
geom_line(color = "steelblue") +
geom_point(color = "steelblue", size = 2) +
scale_x_discrete() +
labs(
title = "PM2.5 por hora el 2022-08-18 (Origen: València - Pista de Silla)",
x = "Hora",
y = expression(PM[2.5]~"(µg/m"^3*")")
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Otra vez, observamos lo mismo que antes, lo cual es raro.
Quinto, esatcion València Port Moll Trans. Ponent y
fecha 2022-08-18.
valores_origen <- df_sin_DANA %>%
filter(
Fecha == as.Date("2022-08-18"),
Origen == "València Port Moll Trans. Ponent"
) %>%
mutate(Hora = factor(Hora, levels = unique(Hora))) %>%
select(Hora, `PM2.5`) %>%
arrange(as.integer(as.character(Hora)))
ggplot(valores_origen, aes(x = Hora, y = `PM2.5`, group = 1)) +
geom_line(color = "steelblue") +
geom_point(color = "steelblue", size = 2) +
scale_x_discrete() +
labs(
title = "PM2.5 por hora el 2022-08-18 (Origen: València Port Moll Trans. Ponent)",
x = "Hora",
y = expression(PM[2.5]~"(µg/m"^3*")")
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Volvemos a observar el mismo comportamiento de antes. Vamos a ver si la otra estacion tambien.
Sexta, estacion València Port llit antic Túria y fecha
2022-08-18.
valores_origen <- df_sin_DANA %>%
filter(
Fecha == as.Date("2022-08-18"),
Origen == "València Port llit antic Túria"
) %>%
mutate(Hora = factor(Hora, levels = unique(Hora))) %>%
select(Hora, `PM2.5`) %>%
arrange(as.integer(as.character(Hora)))
ggplot(valores_origen, aes(x = Hora, y = `PM2.5`, group = 1)) +
geom_line(color = "steelblue") +
geom_point(color = "steelblue", size = 2) +
scale_x_discrete() +
labs(
title = "PM2.5 por hora el 2022-08-18 (Origen: València Port llit antic Túria)",
x = "Hora",
y = expression(PM[2.5]~"(µg/m"^3*")")
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Y como esperábamos, todas las estaciones con valores altos el 18 de Agosto del 2022, se comportan igual. Con un valor altísimo a la 1 de la madrugada y luego valores normales.
Esto resulta muy raro ya que no tiene sentido un cambio tan drástico en los valores de PM10, por lo que podría deberse de un error. De todas formas, el hecho de que varias estaciones sufran lo mismo, hace pensar que pueda haber una razón detrás, ya sea algo real (meteorología, incendio,…) o fue un error general.
Vamos a ver si sucede lo mismo para la variable PM10:
estaciones <- c(
"València - Centre",
"València Port llit antic Túria",
"València - Pista de Silla",
"València Port Moll Trans. Ponent",
"València Olivereta"
)
# 4. Filtra para el 18 de agosto de 2022 y esas estaciones
df_sel <- df_sin_DANA %>%
select(Origen, Fecha, Hora, PM10) %>%
filter(
Fecha == as_date("2022-08-18"),
Origen %in% estaciones,
Hora == 1
)
df_sel
## Origen Fecha Hora PM10
## 1 València Olivereta 2022-08-18 1 337
## 2 València Port Moll Trans. Ponent 2022-08-18 1 242
## 3 València - Pista de Silla 2022-08-18 1 261
## 4 València Port llit antic Túria 2022-08-18 1 284
## 5 València - Centre 2022-08-18 1 325
Podemos observar que en esa fecha y esa hora, la variable PM10 tambien registro valores más altos de lo normal. Esto hace pensar que son valores reales. O tambien podría ser un error general en estas estaciones ya que un aumento tan extremo en tan solo 1 hora es muy raro.
Ya que saber la verdadera razón de esto es muy complicado, no las tocaremos y ya más adelante veremos si hay relación con meteorología, tráfico,…
Distribución:
Al igual que antes, vamos a ver la distribución de PM2.5 durante la época ‘normal’, es decir sin la DANA.
ggplot(df_sin_DANA, aes(x = PM2.5)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de PM2.5",
x = expression(PM[2.5]~"(µg/"*m^3*")"),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 261014 rows containing non-finite outside the scale range
## (`stat_bin()`).
Al excluir la DANA, la distribución parece haberse suavizado un poco, pero sigue estando sesgada a la derecha. Ahora vamos a aplicar una transformación logaritmica para ver si la distribución se normaliza un poco.
df_sin_DANA$log_PM2.5 <- log(df_sin_DANA$PM2.5 + 1)
ggplot(df_sin_DANA, aes(x = log_PM2.5)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de log(PM2.5)",
x = expression(log(PM[2.5]~"(µg/"*m^3*")")),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 261014 rows containing non-finite outside the scale range
## (`stat_bin()`).
Pese a que ahora se asemeja más a una distribución normal, sigue teniendo cierta asimetría. Ahora vamos a ver el comportamiento de PM2.5 durante la DANA.
ggplot(df_DANA, aes(x = PM2.5)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de PM2.5",
x = expression(PM[2.5]~"(µg/"*m^3*")"),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 5626 rows containing non-finite outside the scale range
## (`stat_bin()`).
Durante la DANA, los valores de PM2.5 estan un poco menos sesgados a la derecha, vamos a aplicar la transformación logaritmica para ver si se normaliza un poco.
df_DANA$log_PM2.5 <- log(df_DANA$PM2.5 + 1)
ggplot(df_DANA, aes(x = log_PM2.5)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de log(PM2.5)",
x = expression(log(PM[2.5]~"(µg/"*m^3*")")),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 5626 rows containing non-finite outside the scale range
## (`stat_bin()`).
Sucede algo similar que excluyendo la DANA, la distribución se asimila más a una normal, pero sigue teniendo cierta asimetría, ambos casos tienen problemas en la cola izquierda.
Ahora vamos a ver la distribución logaritmica de todo junto, es decir con la DANA.
df$log_PM2.5 <- log(df$PM2.5 + 1)
ggplot(df, aes(x = log_PM2.5)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de log(PM2.5)",
x = expression(log(PM[2.5]~"(µg/"*m^3*")")),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 266645 rows containing non-finite outside the scale range
## (`stat_bin()`).
Otra vez, tenemos una distribución parecida a la normal, pero con problemas en la cola izquierda.
Vamos a ver la distribución del contaminante por zonas.
df %>%
filter(!is.na(PM2.5)) %>%
ggplot(aes(x = PM2.5)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de PM2.5 por estación",
x = "PM2.5 (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
Sucede lo mismo que en la variable PM2.5, hay algunas zonas con colas mayores en las estaciones afectadas por la DANA. Vamos a ver la ditribucion logaritmica de PM2.5.
df %>%
filter(!is.na(log_PM2.5)) %>%
ggplot(aes(x = log_PM2.5)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de log(PM2.5) por estación",
x = "log(PM2.5) (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
Observamos que muchas zonas se siguen distribuyendo igual, con una concentración mayor en valores altos que bajos. Parece ser que en algunos casos la zona si que influye.
Vamos a ver gráficamente las medias de PM2.5 para cada estación para asi ver si varia mucho los valores.
df_pm2.5 <- df %>%
filter(Origen %in% estaciones_pm10)
# 2) Calcular la media de PM10 por estación (ignorando los NA)
media_por_estacion <- df_pm2.5 %>%
group_by(Origen) %>%
summarise(
media_PM2.5 = mean(PM2.5, na.rm = TRUE),
n_obs = sum(!is.na(PM2.5))
) %>%
ungroup() %>%
arrange(desc(media_PM2.5)) %>%
mutate(Origen = factor(Origen, levels = Origen))
# 3) Gráfico de barras
ggplot(media_por_estacion, aes(x = Origen, y = media_PM2.5)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(media_PM2.5, 1)),
vjust = -0.5, size = 3) +
labs(
title = "Media de PM2.5 por estación",
subtitle = "Solo estaciones que miden PM2.5",
x = "Estación",
y = expression(Media~PM[2.5]~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(media_por_estacion$Origen, "=", media_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Observamos, al igual que antes, valores promedio muy altos en las primeras 4 estaciones:
Estas estaciones son las más afectadas por la dana, y además ya vimos que solo tienen valores en 2024. Por lo que sus medias alteran el resultado ya que son casos excepcionales. Vamos a hacer lo mismo pero omitiendo la DANA.
df_pm2.5 <- df %>%
filter(Origen %in% estaciones_pm10_sin_DANA)
media_por_estacion <- df_pm2.5 %>%
group_by(Origen) %>%
summarise(
media_PM2.5 = mean(PM2.5, na.rm = TRUE),
n_obs = sum(!is.na(PM2.5))
) %>%
ungroup() %>%
arrange(desc(media_PM2.5)) %>%
mutate(Origen = factor(Origen, levels = Origen))
ggplot(media_por_estacion, aes(x = Origen, y = media_PM2.5)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(media_PM2.5, 1)),
vjust = -0.5, size = 3) +
labs(
title = "Media de PM2.5 por estación sin la DANA",
subtitle = "Solo estaciones que miden PM2.5",
x = "Estación",
y = expression(Media~PM[2.5]~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(media_por_estacion$Origen, "=", media_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
En este caso observamos algo similar a PM10 pero València Molí del Sol tiene la media más añta en PM2.5, mientras que en PM10 era la segunda más baja.
Vamos a ver que sucede con las medianas.
df_pm2.5 <- df %>%
filter(Origen %in% estaciones_pm10_sin_DANA)
mediana_por_estacion <- df_pm2.5 %>%
group_by(Origen) %>%
summarise(
mediana_PM2.5 = median(PM2.5, na.rm = TRUE),
n_obs = sum(!is.na(PM2.5))
) %>%
ungroup() %>%
arrange(desc(mediana_PM2.5)) %>%
mutate(Origen = factor(Origen, levels = Origen))
ggplot(mediana_por_estacion, aes(x = Origen, y = mediana_PM2.5)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(mediana_PM2.5, 1)),
vjust = -0.5, size = 3) +
labs(
title = "Mediana de PM2.5 por estación sin la DANA",
subtitle = "Solo estaciones que miden PM2.5",
x = "Estación",
y = expression(Media~PM[2.5]~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(mediana_por_estacion$Origen, "=", mediana_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Observamos una estructura similar a la de PM10. Vemos que la mediana tiene más o menos la misma varianza que la media.
Vamos a hacer lo mismo pero para la desviación típica.
df_pm2.5 <- df %>%
filter(Origen %in% estaciones_pm10_sin_DANA)
sd_por_estacion <- df_pm2.5 %>%
group_by(Origen) %>%
summarise(
sd_PM2.5 = sd(PM2.5, na.rm = TRUE),
n_obs = sum(!is.na(PM2.5))
) %>%
ungroup() %>%
arrange(desc(sd_PM2.5)) %>%
mutate(Origen = factor(Origen, levels = Origen))
ggplot(sd_por_estacion, aes(x = Origen, y = sd_PM2.5)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(sd_PM2.5, 2)),
vjust = -0.5, size = 3) +
labs(
title = "Desviación típica de PM2.5 por estación",
subtitle = "Solo estaciones que miden PM2.5",
x = "Estación",
y = expression(SD~PM2.5~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(sd_por_estacion$Origen, "=", sd_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
La estación Molí del Sol, tiene una desviación típica algo maás significativa que el resto de estaciones, lo que indica que sus valores fluctuan más mientras que en las otras son más constantes.
Ahora vamos a ver los valores máximos y minimos de cada estación.
df_pm2.5 <- df %>%
filter(Origen %in% estaciones_pm10_sin_DANA)
resumen_pm2.5 <- df_pm2.5 %>%
group_by(Origen) %>%
summarise(
min_PM2.5 = min(PM2.5, na.rm = TRUE),
max_PM2.5 = max(PM2.5, na.rm = TRUE)
) %>%
ungroup() %>%
arrange(Origen)
print(resumen_pm2.5)
## # A tibble: 10 × 3
## Origen min_PM2.5 max_PM2.5
## <chr> <int> <int>
## 1 Quart de Poblet 1 132
## 2 Torrent-El Vedat 0 85
## 3 València - Av. França 1 157
## 4 València - Centre 1 296
## 5 València - Molí del Sol 1 135
## 6 València - Pista de Silla 1 249
## 7 València - Politècnic 1 176
## 8 València Olivereta 1 309
## 9 València Port Moll Trans. Ponent 0 263
## 10 València Port llit antic Túria 1 258
ggplot(resumen_pm2.5, aes(x = Origen)) +
geom_linerange(aes(ymin = min_PM2.5, ymax = max_PM2.5), size = 1.5) +
geom_point(aes(y = min_PM2.5), shape = 21, fill = "white", size = 3) +
geom_point(aes(y = max_PM2.5), shape = 21, fill = "black", size = 3) +
coord_flip() +
labs(
title = "Rango de valores de PM2.5 por estación",
x = "Estación",
y = expression(PM[2.5]~"(µg/m³)")
) +
theme_minimal(base_size = 12)
Hay muchas estaciones que tienen rangos similares para PM2.5 como para PM10, pero hay otros que sus rangos son mucho menores, como por ejemplo Quart de Poblet.
Ahora, vamos a estudiar si hay diferencias significativas en los valores de PM2.5 dependiendo de la estación de medición. Para ello, podríamos aplicar un ANOVA, pero dado que son series temporales, vamos a coger una muestra de 200 valores para mejorar las hipotesis del ANOVA.
set.seed(123) # Para reproducibilidad
df_pm2.5 = df_sin_DANA%>%
filter(FechaHora >= "2023-01-01" & FechaHora < "2024-01-01") %>%
filter(hour(FechaHora) == 12) %>%
filter(wday(FechaHora) == 2) %>%
sample_n(200)
df_pm2.5$log_PM2.5 = log(df_pm2.5$PM2.5)
fit_pm2.5 <- aov(log_PM2.5 ~ Origen, data = df_pm2.5)
summary(fit_pm2.5)
## Df Sum Sq Mean Sq F value Pr(>F)
## Origen 9 18.16 2.0175 3.336 0.00117 **
## Residuals 115 69.55 0.6048
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 75 observations deleted due to missingness
El valor de F y el p-value indican que podemos rechazar, con confianza, la hipotesis nula. Es decir, hay diferencias significativas entre las medias de los grupos, en este caso las estaciones de medición.
Antes de ver a que niveles existen diferenias significativas o no, vamos a comprobar las hipotesis del ANOVA. Primero vamos a comprobar la independencia de los residuos, lo haremos con el test de Durbin-Watson.
dw <- dwtest(fit_pm2.5)
print(dw)
##
## Durbin-Watson test
##
## data: fit_pm2.5
## DW = 1.9259, p-value = 0.3194
## alternative hypothesis: true autocorrelation is greater than 0
El hecho de que el valor DW sea cercano a 2, indica ausencia de autocorrelación de primer grado. Además, el p-value es mucho mayor que 0.05, por lo que no hay información suficiente para rechazar la hipotesis nula de no haber autocorrelación positiva. Debido a eso se cumple la condición de independencia.
Ahora vamos a comprobar la homogeniedad.
leveneTest(fit_pm2.5)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 9 2.319 0.01952 *
## 115
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Como el p-value es menor que 0.05, rechazamos la hipotesis nula y por tanto no se cumple la condición de homogeniedad del ANOVA. Debido a eso vamos a hacer un ANOVA de Welch, ya que no asume varianzas iguales.
oneway.test(log_PM2.5 ~ Origen, data = df_pm2.5, var.equal = FALSE)
##
## One-way analysis of means (not assuming equal variances)
##
## data: log_PM2.5 and Origen
## F = 3.2639, num df = 9.000, denom df = 20.664, p-value = 0.01253
La hipótesis nula es que todas las medias de log_PM2.5 son iguales independientemente del origen. Como el p-value es menor que 0.05, rechazamos la hipotesis nula, y por tanto existe al menos un par de origenes cuyas medias difieren significativamente.
tukey_pm2.5 <- TukeyHSD(fit_pm2.5)
res <- as.data.frame(tukey_pm2.5$Origen)
res_ns <- subset(res, `p adj` <= 0.05)
print(res_ns)
## diff lwr upr
## València - Molí del Sol-Quart de Poblet -0.9416055 -1.874136 -0.009074446
## València - Molí del Sol-València - Centre -0.9417757 -1.874307 -0.009244731
## p adj
## València - Molí del Sol-Quart de Poblet 0.04576558
## València - Molí del Sol-València - Centre 0.04568919
Esto muestra que tan solo 3 pares de estaciones tienen diferencias significativas entre ellas, todas tienen como factor comun la zona Quart de Poblet. Son las mismas que en PM10.
Parecido a PM10, las zonas mas afectadas por la DANA tienen valores bastante mas altos. Al igual que la zona de Quart de Poblet. Pero cabe destacar que
Con esto podemos concluir que, los valores de PM2.5 no varían dependiendo de la zona, excepto las estaciones que se vieron afectadas por la DANA en 2024.
Vamos a ver como se comporta el contaminante cada año Para ello, vamos a hacer un gráfico.
df_anual_pm2.5 <- df_sin_DANA %>%
mutate(Año = as.integer(as.character(Año))) %>%
group_by(Año) %>%
summarise(media_PM2.5 = mean(PM2.5, na.rm = TRUE)) %>%
arrange(Año)
ggplot(df_anual_pm2.5, aes(x = Año, y = media_PM2.5)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(
breaks = 2019:2024,
limits = c(2019, 2024)
) +
labs(
title = "Yearly evolution of the average PM2.5 concentration",
subtitle = "Average values from all stations in Valencia",
x = "Year",
y = "Average of PM2.5 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Observamos que de 2019 a 2020 hubo un decrecimiento, esto puede deberse a la pandemia y sus restricciones. Luego paarece que se ha mantenido más o menos constante.
Tambien observamos que a partir de 2022, la concentración media comienza a disminuir, lo cual puede deberse a que la UE esta intentando reducir las emisiones de CO2. Las emisiones de CO2 estan relacionadas con PM2.5 ya que crea un efecto invernadero y no deja que el contaminante se disperse correctamente en la atmosfera.
Vamos a hacer los mismo pero con las desviaciones típicas.
df_anual_sd <- df_sin_DANA %>%
group_by(Año) %>%
summarise(sd_PM2.5 = sd(PM2.5, na.rm = TRUE)) %>%
mutate(Año = as.numeric(as.character(Año))) %>%
arrange(Año)
ggplot(df_anual_sd, aes(x = Año, y = sd_PM2.5)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(
breaks = 2019:2024,
limits = c(2019, 2024)) +
labs(
title = "Yearly evolution of the standard deviation of PM2.5 concentration",
subtitle = "Average values from all stations in Valencia",
x = "Year",
y = "SD of PM2.5 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Observamos que la desviación típica de PM2.5 va disminuyendo a partir de 2019 hasta 2024, debido a que la media tambien va disminuyendo al cabo de los años. Lo más llamativo es que las desviaciones tipicas proporcinalmente, son superiores en 2020 y 2021. Esto se puede deber a que durante la pandemia habia muy poco tráfico rodado y los pocos automoviles que circulaban aumentaban los valores considerablemente, generando asi una mayor desviacion típica.
Podemos concluir que los valores de PM2.5 si dependen de el año. Pero a la vez, su varibilidad es proporcionalmente parecida en todos los años.
df_mensual <- df_sin_DANA %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
group_by(Mes) %>%
summarise(media_PM2.5 = mean(PM2.5, na.rm = TRUE)) %>%
arrange(Mes) %>%
mutate(mes_num = as.integer(Mes))
ggplot(df_mensual, aes(x = mes_num, y = media_PM2.5)) +
geom_area(fill = "#FFEB3B", alpha = 0.4) +
geom_line(color = "#FFC107", size = 1.2) +
geom_point(color = "#FFC107", size = 3) +
scale_x_continuous(
breaks = 1:12,
labels = meses_orden,
limits = c(1, 12)
) +
labs(
title = "Evolución anual de la concentración promedio de PM2.5",
subtitle = "Promedios mensuales desde todas las estaciones en Valencia",
x = "Mes",
y = "Promedio de PM2.5 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#FFC107"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Observamos una gráfica muy similar a la generada con la variable PM10, lo cual tiene sentido ya que estos contaminantes son muy parecidos. En este caso, parece ser que en temperaturas extremas, es decir o mucho calor o mucho frío, los niveles aumentan, esto se estudiará más adelante.
Ahora vamos a ver como son las desviaciones típicas de PM2.5 para cada mes.
df_mensual_sd <- df_sin_DANA %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
group_by(Mes) %>%
summarise(sd_PM2.5 = sd(PM2.5, na.rm = TRUE)) %>%
arrange(Mes) %>%
mutate(mes_num = as.integer(Mes))
ggplot(df_mensual_sd, aes(x = mes_num, y = sd_PM2.5)) +
geom_area(fill = "#FFEB3B", alpha = 0.4) +
geom_line(color = "#FFC107", size = 1.2) +
geom_point(color = "#FFC107", size = 3) +
scale_x_continuous(
breaks = 1:12,
labels = meses_orden,
limits = c(1, 12)
) +
labs(
title = "Evolución anual de la desviacion típica de PM2.5",
subtitle = "Desviaciones típicas mensuales desde todas las estaciones en Valencia",
x = "Mes",
y = "Desviación típica de PM2.5 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#FFC107"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Podemos observar una desviación típica constante a lo largo de los meses. Además, podemos concluir que los meses del año si que afectan a los valores de la variable PM2.5, probablemente relacionado con la temperatura.
df_semanal_pm2.5 <- df_sin_DANA %>%
mutate(
Dia_Semana = factor(Dia_Semana, levels = dias_orden, ordered = TRUE)
) %>%
group_by(Dia_Semana) %>%
summarise(media_PM2.5 = mean(PM2.5, na.rm = TRUE)) %>%
arrange(Dia_Semana) %>%
mutate(dia_num = as.integer(Dia_Semana))
ggplot(df_semanal_pm2.5, aes(x = dia_num, y = media_PM2.5)) +
geom_area(fill = "#2196F3", alpha = 0.4) +
geom_line(color = "#1976D2", size = 1.2) +
geom_point(color = "#1976D2", size = 3) +
scale_x_continuous(
breaks = 1:7,
labels = dias_orden,
limits = c(1, 7)
) +
labs(
title = "Evolución semanal de la concentración promedio de PM2.5",
subtitle = "Promedios semanales desde todas las estaciones en Valencia",
x = "Día de la semana",
y = "Promedio de PM2.5 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#1976D2"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
En este caso, parece ser que de martes a sabado los niveles de PM2.5 son similares pero que el domingo diminuyen considerablemente.
Vamos a hacer lo mismo para la desviación típica de la variable.
df_semanal_sd <- df_sin_DANA %>%
mutate(
Dia_Semana = factor(Dia_Semana, levels = dias_orden, ordered = TRUE)
) %>%
group_by(Dia_Semana) %>%
summarise(sd_PM2.5 = sd(PM2.5, na.rm = TRUE)) %>%
arrange(Dia_Semana) %>%
mutate(dia_num = as.integer(Dia_Semana))
ggplot(df_semanal_sd, aes(x = dia_num, y = sd_PM2.5)) +
geom_area(fill = "#2196F3", alpha = 0.4) +
geom_line(color = "#1976D2", size = 1.2) +
geom_point(color = "#1976D2", size = 3) +
scale_x_continuous(
breaks = 1:7,
labels = dias_orden,
limits = c(1, 7)
) +
labs(
title = "Evolución semanal de la desviación típica de PM2.5",
subtitle = "Desviaciones típicas semanales desde todas las estaciones en Valencia",
x = "Día de la semana",
y = "DS de PM2.5 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#1976D2"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Observamos que los lunes y martes tienen una desviación típica proporcional, menor que el resto de dias. Indicando que las mediciones de esos dias son más constantes.
Concluimos que si que existen diferencias segnificativas en los valores de PM2.5 dependiendo de el dia de la semana, probablemente debido al tráfico rodado.
df_hourly_pm2.5 <- df_sin_DANA %>%
group_by(Hora) %>%
summarise(media_PM2.5 = mean(PM2.5, na.rm = TRUE)) %>%
arrange(Hora)
ggplot(df_hourly_pm2.5, aes(x = factor(Hora, levels = 0:23), y = media_PM2.5, group = 1)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_discrete(drop = FALSE) +
labs(
title = "Hourly evolution of the average PM2.5 concentration",
subtitle = "Average values from all stations in Valencia",
x = "Hour of the day",
y = "Average of PM2.5 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Sorprendentemente, el comportamiento no se parece al de PM10. En este caso los valores disminuyen a las 12 del mediodía, e incrementan a las 16 de la tarde. Tiene sentido ya que las horas punta son cuando la mayoría de gente entra o sale de trabajar. Esto se verá más adelante.
Ahora vamos a ver como varían las desviaciones típicas.
df_hourly_sd <- df_sin_DANA %>%
group_by(Hora) %>%
summarise(sd_PM2.5 = sd(PM2.5, na.rm = TRUE)) %>%
mutate(Hora = as.numeric(as.character(Hora))) %>%
arrange(Hora)
ggplot(df_hourly_sd, aes(x = Hora, y = sd_PM2.5)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(breaks = 0:23, limits = c(0, 23)) +
labs(
title = "Hourly evolution of the strandard deviation of PM2.5",
subtitle = "Standard deviation values from all stations in Valencia",
x = "Hour of the day",
y = "SD of PM2.5 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Se puede destacar que las horas entre las 5 y las 10 tienen una desviación típica proporcional menor a el resto de valores. Lo que indica que a esa hora los valores de PM2.5 tienden a ser más constantes.
Concluimos que, la hora del día si que afecta a los valores de PM2.5, probablemente debido al tráfico rodado en la ciudad.
Primero de todo, vamos a ver el porcentaje de faltantes.
sum(is.na(df$PM2.5)) / nrow(df) * 100
## [1] 39.87233
Tiene el mismo porcentaje de faltantes que PM10, y se mide en las mismas esatciones que PM10, como vimos en la preparacion de la base de datos. Se mide en todas las estaciones excepto:
Vamos a ver el porcentaje de faltantes en las estaciones que la miden.
resumen_total <- df %>%
filter(Origen %in% estaciones_pm10) %>%
summarise(
total_obs = n(),
faltantes = sum(is.na(PM2.5)),
pct_faltantes = faltantes / total_obs * 100
)
print(resumen_total)
## total_obs faltantes pct_faltantes
## 1 421902 19800 4.693033
Observamos que hay un 0.7% menos de faltantes en PM2.5 que en PM10.
Ahora que hemos visto sus distribuciones, es probable que las variables se midan en un mismo sensor, por lo que los faltantes estarían distribuidos igual en ambas variables.
Vamos a ver si los faltantes por año son iguales:
df_plot <- df %>%
filter(
Origen %in% estaciones_pm10,
is.na(PM2.5)
) %>%
count(Año, name = "n_missing_PM2.5") %>%
ungroup() %>%
mutate(
total_missing_PM2.5 = sum(n_missing_PM2.5),
pct_total = n_missing_PM2.5 / total_missing_PM2.5 * 100
) %>%
arrange(Año)
ggplot(df_plot, aes(x = factor(Año), y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de PM2.5 por año",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese año",
x = "Año",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
Como habíamos supuesto, la distribución de los faltantes de la variable PM2.5 por año, es similar a la de PM10. Con una mayor concentración en el año 2023.
Ahora vamos a ver su distribucion por meses para ver la diferencia entre PM10 y PM2.5.
df_plot <- df %>%
filter(Origen %in% estaciones_pm10) %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
filter(is.na(PM2.5)) %>%
count(Mes, name = "n_missing") %>%
ungroup() %>%
mutate(
total_missing = sum(n_missing),
pct_total = n_missing / total_missing * 100
)
# 2) Gráfico
ggplot(df_plot, aes(x = Mes, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de PM2.5 por mes",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese mes",
x = "Mes",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5))
En el caso de los meses, la distribucion de los faltantes de ambos contaminantes son similares, en este caso estando un poco mas distribuidos.
Ahora vamos a hacer lo mismo para dias de la semana, que en PM10 estaban más o menos uniformemente distribuidos.
df_plot <- df %>%
filter(
Origen %in% estaciones_pm10,
is.na(PM2.5)
) %>%
count(Dia_Semana, name = "n_missing") %>%
ungroup() %>%
mutate(
total_missing = sum(n_missing),
pct_total = n_missing / total_missing * 100
) %>%
mutate(Dia_Semana = factor(Dia_Semana, levels = dias_orden)) %>%
arrange(Dia_Semana)
# 2) Gráfico
ggplot(df_plot, aes(x = Dia_Semana, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de PM2.5 por día de la semana",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese día",
x = "Día de la semana",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle= element_text(hjust = 0.5)
)
En este caso, volvemos a observar una distribución de los faltantes similar a la de PM10.
Por último veamos si por horas tienen una distribución similar, en PM10 era uniforme.
df_plot <- df %>%
filter(
Origen %in% estaciones_pm10,
is.na(PM2.5)
) %>%
count(Hora, name = "n_missing") %>%
ungroup() %>%
mutate(
total_missing = sum(n_missing),
pct_total = n_missing / total_missing * 100 #
) %>%
arrange(Hora)
ggplot(df_plot, aes(x = Hora, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
scale_x_continuous(breaks = 0:23) +
labs(
title = "Porcentaje de valores faltantes de PM2.5 por hora del día",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en esa hora",
x = "Hora",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle= element_text(hjust = 0.5)
)
Como esperábamos, tienen una distribución muy similar. El hecho de que tengan muchas similitudes en las distribuciones de los faltantes puede indicar un patrón.
Para si hay un patrón vamos a ver si un faltante se repite en otros años. Esto lo vamos a hacer siguiendo estos pasos:
-filtrar estaciones que miden la variable - Seleccionar filas donde la variable es NA - Agrupar por combinación de Mes, Dia y Hora - Calcular el numero de años distintos con al menos ese NA y contruimos la lista de años - Solo nos quedamos con los que se repiten en más de un año
df_faltantes_pm2.5 <- df %>%
filter(Origen %in% estaciones_pm10)
faltantes_recurrentes_pm2.5 <- df_faltantes_pm2.5 %>%
filter(is.na(PM2.5)) %>%
group_by(Mes, Dia, Hora) %>%
summarise(
n_años = n_distinct(Año),
Años_con_faltante = paste(sort(unique(Año)), collapse = ", "),
.groups = "drop"
) %>%
filter(n_años > 1) %>%
select(Mes, Dia, Hora, Años_con_faltante)
print(faltantes_recurrentes_pm2.5)
## # A tibble: 5,759 × 4
## Mes Dia Hora Años_con_faltante
## <chr> <int> <int> <chr>
## 1 Abril 5 0 2022, 2023
## 2 Abril 5 7 2022, 2023
## 3 Abril 5 8 2022, 2023
## 4 Abril 5 9 2022, 2023
## 5 Abril 7 13 2022, 2023
## 6 Abril 8 16 2023, 2024
## 7 Abril 8 17 2023, 2024
## 8 Abril 8 18 2023, 2024
## 9 Abril 8 19 2023, 2024
## 10 Abril 8 20 2023, 2024
## # ℹ 5,749 more rows
Podemos observar que si parece haber un patrón en los faltantes, ya que hay muchas fechas con el mismo faltante pero en otros años.
df %>%
mutate(missing = is.na(PM2.5)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c('TRUE' = "red", 'FALSE' = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de PM2.5",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
df %>%
filter(Origen %in% estaciones_pm10) %>%
mutate(missing = is.na(PM2.5)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c('TRUE' = "red", 'FALSE' = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de PM2.5",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
Gracias a esta gráfica podemos ver más facilmente el patrón de faltantes de la variable PM2.5. Podemos también ver en que estaciones se ha medido la variable, su proporción de faltantes,… Tambien vemos que la distribución de los faltantes en PM2.5 es muy similar a la de PM10, como ya habiamos mencionado antes.
Aqui vamos a ver la comparación de PM10 y PM2.5 ya que son el mismo contaminante pero con diferentes tamaños de particulas.
df_pm_anual <- df_sin_DANA %>%
group_by(year = year(FechaHora)) %>%
summarise(across(c("PM10", "PM2.5"), mean, na.rm = TRUE)) %>%
filter(!is.na(year)) %>%
ungroup()
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `across(c("PM10", "PM2.5"), mean, na.rm = TRUE)`.
## ℹ In group 1: `year = 2019`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
##
## # Previously
## across(a:b, mean, na.rm = TRUE)
##
## # Now
## across(a:b, \(x) mean(x, na.rm = TRUE))
df_long <- df_pm_anual %>%
pivot_longer(
cols = c("PM10", "PM2.5"),
names_to = "Gas",
values_to = "Concentracion"
)
# 2) Gráfico con áreas superpuestas, líneas y puntos
ggplot(df_long, aes(x = year, y = Concentracion,
group = Gas, fill = Gas, color = Gas)) +
geom_area(alpha = 0.4, position = "identity") +
geom_line(size = 1.2) +
geom_point(size = 3) +
scale_x_continuous(breaks = 2019:2024, limits = c(2019, 2024)) +
scale_fill_manual(
values = c(
PM10 = "#FFD580",
PM2.5 = "#6EC6FF"
)
) +
scale_color_manual(
values = c(
PM10 = "#FF8C00",
PM2.5 = "#4682B4"
)
) +
labs(
title = "Evolución anual de la concentración media de PM10 y PM2.5",
subtitle = "Valores medios de todas las estaciones en Valencia",
x = "Año",
y = "Concentración media (µg/m³)",
fill = "Gas",
color = "Gas",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60"),
legend.position = "bottom"
)
df_pm_mensual <- df_sin_DANA %>%
mutate(
month = month(FechaHora, label = TRUE, abbr = TRUE)
) %>%
group_by(month) %>%
summarise(
PM10 = mean(PM10, na.rm = TRUE),
PM2.5 = mean(PM2.5, na.rm = TRUE),
.groups = "drop"
) %>%
pivot_longer(
cols = c("PM10","PM2.5"),
names_to = "Gas",
values_to = "Concentracion"
)
#slice(1:(n() - 3))
# 2) Gráfico tipo área/línea/punto por mes
ggplot(df_pm_mensual,
aes(x = month, y = Concentracion,
group = Gas, fill = Gas, color = Gas)) +
geom_area(alpha = 0.4, position = "identity") +
geom_line(size = 1.2) +
geom_point(size = 3) +
scale_fill_manual(
values = c(
PM10 = "#FFD580",
PM2.5 = "#6EC6FF"
)
) +
scale_color_manual(
values = c(
PM10 = "#FF8C00",
PM2.5 = "#4682B4"
)
) +
labs(
title = "Evolución media mensual de PM10 y PM2.5",
subtitle = "Media agregada sobre todos los años",
x = "Mes",
y = "Concentración media (µg/m³)",
fill = "Gas",
color = "Gas",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60"),
legend.position = "bottom"
)
dias_orden <- c("lunes","martes","miércoles","jueves","viernes","sábado","domingo")
df_pm_diario <- df_sin_DANA %>%
mutate(
Dia_Semana = factor(Dia_Semana, levels = dias_orden, ordered = TRUE)
) %>%
group_by(Dia_Semana) %>%
summarise(
PM10 = mean(PM10, na.rm = TRUE),
PM2.5 = mean(PM2.5, na.rm = TRUE),
.groups = "drop"
) %>%
pivot_longer(
cols = c("PM10","PM2.5"),
names_to = "Gas",
values_to = "Concentracion"
)
ggplot(df_pm_diario,
aes(x = Dia_Semana, y = Concentracion,
group = Gas, fill = Gas, color = Gas)) +
geom_area(alpha = 0.4, position = "identity") +
geom_line(size = 1.2) +
geom_point(size = 3) +
scale_fill_manual(
values = c(
PM10 = "#FFD580",
PM2.5 = "#6EC6FF"
)
) +
scale_color_manual(
values = c(
PM10 = "#FF8C00",
PM2.5 = "#4682B4"
)
) +
labs(
title = "Concentración media de PM10 y PM2.5 por día de la semana",
subtitle = "Agregado sobre todos los años",
x = "Día de la semana",
y = "Concentración media (µg/m³)",
fill = "Gas",
color = "Gas",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60"),
legend.position = "bottom"
)
df_pm_horario <- df_sin_DANA %>%
mutate(hour = hour(FechaHora)) %>%
group_by(hour) %>%
summarise(
PM10 = mean(PM10, na.rm = TRUE),
PM2.5 = mean(PM2.5, na.rm = TRUE),
.groups = "drop"
) %>%
pivot_longer(
cols = c("PM10","PM2.5"),
names_to = "Gas",
values_to = "Concentracion"
)
ggplot(df_pm_horario,
aes(x = hour, y = Concentracion,
group = Gas, fill = Gas, color = Gas)) +
geom_area(alpha = 0.4, position = "identity") +
geom_line(size = 1.2) +
geom_point(size = 3) +
scale_x_continuous(breaks = 0:23, limits = c(0,23)) +
scale_fill_manual(
values = c(
PM10 = "#FFD580",
PM2.5 = "#6EC6FF"
)
) +
scale_color_manual(
values = c(
PM10 = "#FF8C00",
PM2.5 = "#4682B4"
)
) +
labs(
title = "Concentración media de PM10 y PM2.5 por hora del día",
subtitle = "Media agregada sobre todos los años",
x = "Hora del día",
y = "Concentración media (µg/m³)",
fill = "Gas",
color = "Gas",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60"),
legend.position = "bottom"
)
ggplot(df_sin_DANA, aes(x = FechaHora)) +
geom_line(aes(y = PM10, color = "PM10")) +
geom_line(aes(y = PM2.5, color = "PM2.5")) +
labs(title = "Contaminantes PM10 y PM2.5 a lo largo del tiempo",
x = "Fecha y Hora",
y = "Concentración (µg/m³)",
color = "Contaminante") +
theme_minimal()
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Removed 3 rows containing missing values or values outside the scale range
## (`geom_line()`).
Observamos que tienen un comportamiento bastante similar.
Ahora vamos a analizar la variable SO2, la cual esta relacionada con las variables PM10 y PM2.5, ya que SO2 es precursor de sulfatos que contribuyen a la formación de material particulado (PM10 y PM10).
hist(df$SO2, breaks = 30, col = "skyblue", main = "Histograma de SO2", xlab = "SO2 (µg/m³)")
Podemos observar una distribución extremadamente sesgada. Esto podría deberse a la presencia de datos atípicos o outliers, que son valores que se desvían significativamente de la tendencia general de los datos. Para confirmar esto, vamos a calcular algunos estadísticos descriptivos.
summary(df$SO2)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.000 3.000 3.000 3.628 4.000 269.000 186240
El valor máximo de la variable es bastante alto en comparación a la media y mediana. Vamos a ver como se distribuye a lo largo del tiempo.
ggplot(df, aes(x = FechaHora, y = SO2, group = 1)) +
geom_line(color = "steelblue") +
scale_x_datetime(
date_breaks = "1 year",
date_labels = "%Y"
) +
labs(
title = "Evolución temporal de SO2",
x = "Año",
y = expression(SO~"(µg/m"^3*")")
) +
theme_minimal()
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).
En este caso parece ser que lso valores de la variable no se vieron afectados por la DANA. Pero se pueden observar unos cuantos picos alrededor de 2021. Vamos a ver que fechas son para poder estudiarlos más a fondo.
fechas_valores_exceso <- df %>%
filter(`SO2` > 100) %>%
select(Fecha, Hora, Origen, `SO2`) %>%
arrange(Fecha)
print(fechas_valores_exceso)
## Fecha Hora Origen SO2
## 1 2020-09-22 10 València Port Moll Trans. Ponent 269
## 2 2020-09-22 11 València Port Moll Trans. Ponent 233
## 3 2020-10-06 8 València Port Moll Trans. Ponent 148
## 4 2020-10-06 9 València Port Moll Trans. Ponent 269
## 5 2020-11-18 11 València Port Moll Trans. Ponent 269
## 6 2021-02-23 9 València Port Moll Trans. Ponent 167
Podemos observar que todos esos valores más elevados provienen de la misma estación, más o menos las mismas horas y meses cercanos. Es decir que podrían ser valores reales y no errores. Además tiene sentido, ya que el SO2 se produce por la combustión de combustibles fosiles en por ejemplo maquinaria pesada presente en puertos, como por ejemplo barcos, gruas, camiones,…
Buscando por internet, hemos encontrado que Octubre, Noviembre y Diciembre el tráfico de contenedores aumento un 20%, lo cual pudo haber generado estos picos. Por eso, vamos a tratarlos como valore reales.
Ahora vamos a ver la distribución de la variable al aplicar una transformación logarítmica, ya que al principio nos sale una distribucion muy sesgada.
df$log_SO2 <- log(df$SO2 + 1)
ggplot(df, aes(x = log_SO2)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de log(SO2)",
x = expression(log(SO[2]~"(µg/"*m^3*")")),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 186240 rows containing non-finite outside the scale range
## (`stat_bin()`).
La distribución mostrada es claramente unimodal y asimetrica a la derecha. Es decir, la mayoría de las medidas de SO2 durante el periodo analizado se sitúan en niveles bajos-moderados, con raras excursiones hacia valores altos que aparecen como la cola derecha del histograma.
Dado que no hay normalidad no podremos aplicar ANOVA. Vamos a realizar un test para comprobar si hay homocedasticidad, para descartar por comleto el ANOVA.
Test de Levene
leveneTest(log_SO2 ~ Origen, data = df)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 14 5813.7 < 2.2e-16 ***
## 482492
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Dado que el p-value es < 0.05, rechazamos hipotesis nula, y por tanto afirmamos heterogeniedad. Como no hay heterocedasticidad, no podemos realizar ANOVA.
Vamos a ver la distribución del contaminante por zonas
df %>%
filter(!is.na(SO2)) %>%
ggplot(aes(x = SO2)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de SO2 por estación",
x = "SO2 (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
Observamos que la distribución de los datos en las zonas no varía casi nada, si se observa valore más altos en las estaciones del puerto, posiblemente debido al tráfico portuario. Vamos a hacer lo mismo pero para log(SO2).
df %>%
filter(!is.na(log_SO2)) %>%
ggplot(aes(x = log_SO2)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de log(SO2) por estación",
x = "log(SO2) (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
En las distribuciones logaritmicas, si que se puede apreciar que existen diferencias significativas en algunas zonas. Esto lo veremos más adelnate.
Vamos a ver gráficamente las medias de SO2 para cada estación para asi ver si varia mucho los valores.
no_tomar <- c("València Olivereta",
"València - Centre",
"València - Nazaret Met-2",
"Benetusser UM",
"València-Conselleria Meteo")
estaciones_so2 <- setdiff(unique(df$Origen), no_tomar)
df_so2 <- df %>%
filter(Origen %in% estaciones_so2)
media_por_estacion <- df_so2 %>%
group_by(Origen) %>%
summarise(
media_SO2 = mean(SO2, na.rm = TRUE),
n_obs = sum(!is.na(SO2))
) %>%
ungroup() %>%
arrange(desc(media_SO2)) %>%
mutate(Origen = factor(Origen, levels = Origen))
ggplot(media_por_estacion, aes(x = Origen, y = media_SO2)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(media_SO2, 1)),
vjust = -0.5, size = 3) +
labs(
title = "Media de SO2 por estación",
subtitle = "Solo estaciones que miden SO2",
x = "Estación",
y = expression(Media~SO2~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(media_por_estacion$Origen, "=", media_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Observamos que las medias varian entre 3 y 4 en todas las estaciones excepto en Massanassa, que es de 1.1. El hecho de que la mayoria de medias sean similares nos indica que los valores de SO2 son más estacionales que los de otros contaminantes, lo cual se debe a su ditribución unimodal.
Vamos a ver que sucede con las medianas.
df_so2 <- df %>%
filter(Origen %in% estaciones_so2)
mediana_por_estacion <- df_so2 %>%
group_by(Origen) %>%
summarise(
mediana_SO2 = median(SO2, na.rm = TRUE),
n_obs = sum(!is.na(SO2))
) %>%
ungroup() %>%
arrange(desc(mediana_SO2)) %>%
mutate(Origen = factor(Origen, levels = Origen))
ggplot(mediana_por_estacion, aes(x = Origen, y = mediana_SO2)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(mediana_SO2, 1)),
vjust = -0.5, size = 3) +
labs(
title = "Mediana de SO2 por estación sin la DANA",
subtitle = "Solo estaciones que miden SO2",
x = "Estación",
y = expression(Media~SO2~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(mediana_por_estacion$Origen, "=", mediana_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Parecido a lo sucedido con la media, Massanassa tiene la mediana más baja mientras que el resto de estaciones tienen la misma mediana. Podemos deducir que en Massanassa los niveles de PM2.5 son bastante más bajos que en el resto. Esto vueelve a deberse a la distribución unimodal.
Vamos a hacer lo mismo para la desviación típica
df_so2 <- df %>%
filter(Origen %in% estaciones_so2)
sd_por_estacion <- df_so2 %>%
group_by(Origen) %>%
summarise(
sd_SO2 = sd(SO2, na.rm = TRUE),
n_obs = sum(!is.na(SO2))
) %>%
ungroup() %>%
arrange(desc(sd_SO2)) %>%
mutate(Origen = factor(Origen, levels = Origen))
ggplot(sd_por_estacion, aes(x = Origen, y = sd_SO2)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(sd_SO2, 2)),
vjust = -0.5, size = 3) +
labs(
title = "Desviación típica de SO2 por estación",
subtitle = "Solo estaciones que miden SO2",
x = "Estación",
y = expression(SD~SO2~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(sd_por_estacion$Origen, "=", sd_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Lo que más llama la atención es que las dos estaciones situadas en el puerto, son las que mayor variabilidad tienen. Esto puede deberse a la actividad portuaria, ya que eso causa muchos picos y bajos, dependiendo del tráfico de barcos y camiones.
Además, hay 4 estaciones con unas desviaciones típicas mucho más bajas que las demás. Estas zonas solo tienen valores en 2024 por lo que al haberse medido durante menos tiempo, es dificil que haya mucha variabilidad.
Ahora vamos a ver los valores máximos y minimos de cada estación.
df_so2 <- df %>%
filter(Origen %in% estaciones_so2)
resumen_so2 <- df_so2 %>%
group_by(Origen) %>%
summarise(
min_SO2 = min(SO2, na.rm = TRUE),
max_SO2 = max(SO2, na.rm = TRUE)
) %>%
ungroup() %>%
arrange(Origen)
print(resumen_so2)
## # A tibble: 15 × 3
## Origen min_SO2 max_SO2
## <chr> <int> <int>
## 1 Catarroja UM 3 5
## 2 Massanassa_UM 0 7
## 3 Paiporta UM 3 7
## 4 Paterna - CEAM 3 26
## 5 Quart de Poblet 3 38
## 6 Sedavi UM 3 6
## 7 Torrent-El Vedat 3 22
## 8 València - Av. França 3 21
## 9 València - Bulevard Sud 3 35
## 10 València - Molí del Sol 3 25
## 11 València - Pista de Silla 3 53
## 12 València - Politècnic 3 21
## 13 València - Vivers 3 16
## 14 València Port Moll Trans. Ponent 1 269
## 15 València Port llit antic Túria 1 59
ggplot(resumen_so2, aes(x = Origen)) +
geom_linerange(aes(ymin = min_SO2, ymax = max_SO2), size = 1.5) +
geom_point(aes(y = min_SO2), shape = 21, fill = "white", size = 3) +
geom_point(aes(y = max_SO2), shape = 21, fill = "black", size = 3) +
coord_flip() +
labs(
title = "Rango de valores de SO2 por estación",
x = "Estación",
y = expression(SO2~"(µg/m³)")
) +
theme_minimal(base_size = 12)
Todas la estaciones, excepto València Port Moll Trans Ponent, tienen rangos muy pequeños y en valores muy bajos. La otra parece ser que sufrió algun episodio de contaminación extrema o algun error de medición, ya que valores tan altos son muy inusuales.
Ahora, vamos a estudiar si hay diferencias significativas en los valores de SO2 dependiendo de la estación de medición. Ya que no podemos usar un ANOVA, vamos a utilizar el test de Kruksal-Wallis para comprobar si exite al menos una diferencia entre varios origenes.
kruskal.test(log_SO2 ~ Origen, data = df)
##
## Kruskal-Wallis rank sum test
##
## data: log_SO2 by Origen
## Kruskal-Wallis chi-squared = 25821, df = 14, p-value < 2.2e-16
Dado que el p-values es inferior a 0.05, podemos rechazar la hipotesis nula y confirmar que hay evidencia de diferencias significativas entre distribuciones de SO2 según el Origen.
Ahora vamos a realizar el test de Dunn, para realizar las comparaciones por pares para saber que niveles de Origen difieren.
df %>%
dunn_test(log_SO2 ~ Origen, p.adjust.method = "bonferroni")
## # A tibble: 105 × 9
## .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif
## * <chr> <chr> <chr> <int> <int> <dbl> <dbl> <dbl> <chr>
## 1 log_SO2 Catarr… Massa… 944 1162 -29.0 1.87e-184 1.96e-182 ****
## 2 log_SO2 Catarr… Paipo… 944 850 0.987 3.23e- 1 1 e+ 0 ns
## 3 log_SO2 Catarr… Pater… 944 50288 22.2 8.77e-109 9.21e-107 ****
## 4 log_SO2 Catarr… Quart… 944 48612 22.1 6.07e-108 6.38e-106 ****
## 5 log_SO2 Catarr… Sedav… 944 745 4.26 2.06e- 5 2.17e- 3 **
## 6 log_SO2 Catarr… Torre… 944 11609 3.36 7.77e- 4 8.16e- 2 ns
## 7 log_SO2 Catarr… Valèn… 944 50142 20.5 1.58e- 93 1.66e- 91 ****
## 8 log_SO2 Catarr… Valèn… 944 49294 23.2 3.03e-119 3.18e-117 ****
## 9 log_SO2 Catarr… Valèn… 944 48995 17.4 5.84e- 68 6.13e- 66 ****
## 10 log_SO2 Catarr… Valèn… 944 50113 23.8 3.59e-125 3.76e-123 ****
## # ℹ 95 more rows
Todas las parejas de estaciones tienen diferencias significativas excepto las siguientes parejas:
Podemos observar que muchas de las que no tienen diferencias, y estuvieron afectadas por la DANA la mayoría. Luego las de los puertos tienen valores parecidos a las zonas afectadas por la DANA, debido a que en los puertos se genera mucho SO2 debido a la combustion de los barcos, camiones,…
Podemos concluir que, la zona si influye en las mediciones de SO2. Más adelante estudiaremos si puede deberse al tráfico o meteorología.
Vamos a ver como se comporta el contaminante a lo largo del tiempo. Para ello, vamos a hacer un gráfico.
df_anual_so2 <- df %>%
mutate(Año = as.integer(as.character(Año))) %>%
group_by(Año) %>%
summarise(media_so2 = mean(SO2, na.rm = TRUE)) %>%
arrange(Año)
ggplot(df_anual_so2, aes(x = Año, y = media_so2)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(
breaks = 2019:2024,
limits = c(2019, 2024)
) +
labs(
title = "Yearly evolution of the average SO2 concentration",
subtitle = "Average values from all stations in Valencia",
x = "Year",
y = "Average of SO2 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Para este contaminante tambien se ve una disminución en 2020 pero mucho menos significativa.Pero en lineas generales, parece no variar mucho.
Vamos a hacer lo mismo para las desviaciones típicas.
df_anual_so2_sd <- df %>%
mutate(Año = as.integer(as.character(Año))) %>%
group_by(Año) %>%
summarise(sd_so2 = sd(SO2, na.rm = TRUE)) %>%
arrange(Año)
ggplot(df_anual_so2_sd, aes(x = Año, y = sd_so2)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(
breaks = 2019:2024,
limits = c(2019, 2024)
) +
labs(
title = "Yearly evolution of the standard deviation of SO2",
subtitle = "Standard deviation values from all stations in Valencia",
x = "Year",
y = "SD of SO2 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Observamos que todos los años tienen desviaciones típicas similares, excepto 2020 que tiene una desviación típica proporcional muy alta en comparación a los demas años.
Pero, en lineas generales, podemos asumir que no hay una diferencia significativa en los valores de SO2 dependiendo de año.
df_mensual_so2 <- df %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
group_by(Mes) %>%
summarise(media_so2 = mean(SO2, na.rm = TRUE)) %>%
arrange(Mes) %>%
mutate(mes_num = as.integer(Mes))
ggplot(df_mensual_so2, aes(x = mes_num, y = media_so2)) +
geom_area(fill = "#FFEB3B", alpha = 0.4) +
geom_line(color = "#FFC107", size = 1.2) +
geom_point(color = "#FFC107", size = 3) +
scale_x_continuous(
breaks = 1:12,
labels = meses_orden,
limits = c(1, 12)
) +
labs(
title = "Evolución anual de la concentración promedio de SO2",
subtitle = "Promedios mensuales desde todas las estaciones en Valencia",
x = "Mes",
y = "Promedio de SO2 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#FFC107"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
En este caso, parece que la distribución de los valores de SO2 es mas o menos uniforme, es decir que no varían mucho por el año. Vamos a ver como varían los valores en cada mes.
df_mensual_so2_sd <- df %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
group_by(Mes) %>%
summarise(sd_so2 = sd(SO2, na.rm = TRUE)) %>%
arrange(Mes) %>%
mutate(mes_num = as.integer(Mes))
ggplot(df_mensual_so2_sd, aes(x = mes_num, y = sd_so2)) +
geom_area(fill = "#FFEB3B", alpha = 0.4) +
geom_line(color = "#FFC107", size = 1.2) +
geom_point(color = "#FFC107", size = 3) +
scale_x_continuous(
breaks = 1:12,
labels = meses_orden,
limits = c(1, 12)
) +
labs(
title = "Evolución mensual de la ditribución típica de SO2",
subtitle = "Desviaciónes típicas mensuales desde todas las estaciones en Valencia",
x = "Mes",
y = "SD de SO2 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#FFC107"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Se observa claramente que en los meses de invierno, los valores de SO2 varían mucho más que en los meses de verano. Probablemente debido a la temperatura, lo cual estudiaremos más adelante.
En lineas generales, los valores del SO2 no se ven afectados por el mes del año, pero si que varian más en los meses de invierno.
df_semanal_so2 <- df %>%
mutate(
Dia_Semana = factor(Dia_Semana, levels = dias_orden, ordered = TRUE)
) %>%
group_by(Dia_Semana) %>%
summarise(media_so2 = mean(SO2, na.rm = TRUE)) %>%
arrange(Dia_Semana) %>%
mutate(dia_num = as.integer(Dia_Semana))
ggplot(df_semanal_so2, aes(x = dia_num, y = media_so2)) +
geom_area(fill = "#2196F3", alpha = 0.4) +
geom_line(color = "#1976D2", size = 1.2) +
geom_point(color = "#1976D2", size = 3) +
scale_x_continuous(
breaks = 1:7,
labels = dias_orden,
limits = c(1, 7)
) +
labs(
title = "Evolución semanal de la concentración promedio de SO2",
subtitle = "Promedios semanales desde todas las estaciones en Valencia",
x = "Día de la semana",
y = "Promedio de SO2 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#1976D2"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Parece ser que el valor de SO2 no varía casi segun el dia de la semana. Vamos a ver si los datos varían más o menos en los dias de la semana.
df_semanal_so2_sd <- df %>%
mutate(
Dia_Semana = factor(Dia_Semana, levels = dias_orden, ordered = TRUE)
) %>%
group_by(Dia_Semana) %>%
summarise(sd_so2 = sd(SO2, na.rm = TRUE)) %>%
arrange(Dia_Semana) %>%
mutate(dia_num = as.integer(Dia_Semana))
ggplot(df_semanal_so2_sd, aes(x = dia_num, y = sd_so2)) +
geom_area(fill = "#2196F3", alpha = 0.4) +
geom_line(color = "#1976D2", size = 1.2) +
geom_point(color = "#1976D2", size = 3) +
scale_x_continuous(
breaks = 1:7,
labels = dias_orden,
limits = c(1, 7)
) +
labs(
title = "Evolución semanal de la desviación típica de SO2",
subtitle = "Desviaciones típicas semanales desde todas las estaciones en Valencia",
x = "Día de la semana",
y = "SD de SO2 (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#1976D2"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
A lo largo de la semana los valores de SO2 suelen variar más o menos lo mismo excepto los martes, ya que proporcionalmente tiene una desviación típica superior. Cabe destacar que sucede algo parecido con la variable Año.
Podemos, con certeza, confirmar que SO2 no se ve afectado por los dias de la semana.
df_hourly_so2 <- df %>%
group_by(Hora) %>%
summarise(media_so2 = mean(SO2, na.rm = TRUE)) %>%
arrange(Hora)
ggplot(df_hourly_so2, aes(x = factor(Hora, levels = 0:23), y = media_so2, group = 1)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_discrete(drop = FALSE) +
labs(
title = "Hourly evolution of the average SO2 concentration",
subtitle = "Average values from all stations in Valencia",
x = "Hour of the day",
y = "Average of SO2 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
El contaminante SO2 tiene un comportamiento constante excepto de 7 a 8 que hay un liegro pico. Esto podría deberse a que hay más picos en esas horas. Vamos a ver como varian los valore de SO2 segun la hora.
df_hourly_so2_sd <- df %>%
group_by(Hora) %>%
summarise(sd_so2 = sd(SO2, na.rm = TRUE)) %>%
arrange(Hora)
ggplot(df_hourly_so2_sd, aes(x = factor(Hora, levels = 0:23), y = sd_so2, group = 1)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_discrete(drop = FALSE) +
labs(
title = "Hourly evolution of the stadard deviation SO2 concentration",
subtitle = "Standard deviations values from all stations in Valencia",
x = "Hour of the day",
y = "SD of SO2 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Observamos unas desviaciones tipicas muy altas proporcionalmente en las horas entre las 6 y las 12, lo que puede deberse a la existencia de picos anormales en esas horas. Probalemente devidos al tráfico rodado.
Si que parece haber diferencias significativas para los valores entre las 6 y las 12, en el resto parecen comportarse más o menos igual.
Primero de todo, vamos a ver el porcentaje de faltantes.
sum(is.na(df$SO2)) / nrow(df) * 100
## [1] 27.8491
Esta variable tiene menos valores faltantes que PM10 y PM2.5, vamos a ver en que estaciones tenemos más faltantes.
df %>%
filter(is.na(SO2)) %>%
count(Origen) %>%
arrange(desc(n)) %>%
ggplot(aes(x = reorder(Origen, -n), y = n)) +
geom_bar(stat = "identity", fill = "firebrick") +
labs(title = "Número de valores faltantes (NA) en SO2 por estación",
x = "Estación (Origen)",
y = "Número de NAs") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Resalta que hay varias estaciones con un número elevado de faltantes, lo cual se puede deber a que no se mide SO2 en esas estaciones.
resumen_so2 <- df %>%
select(Origen, SO2) %>%
pivot_longer(
cols = -Origen,
names_to = "Variable",
values_to = "Valor"
) %>%
filter(Variable == "SO2", !is.na(Valor)) %>%
group_by(Variable) %>%
summarise(
num_estaciones = n_distinct(Origen),
lista_estaciones = paste(sort(unique(Origen)), collapse = ", ")
) %>%
ungroup()
print(resumen_so2)
## # A tibble: 1 × 3
## Variable num_estaciones lista_estaciones
## <chr> <int> <chr>
## 1 SO2 15 Catarroja UM, Massanassa_UM, Paiporta UM, Paterna - C…
La variable SO2 no se mide en las siguientes estaciones:
Ahora vamos a ver el los faltantes en las estaciones que si miden SO2.
df_plot <- df %>%
filter(Origen %in% estaciones_so2) %>%
group_by(Origen) %>%
summarise(
n = sum(is.na(SO2)),
total = n(),
pct = n / total * 100
) %>%
arrange(desc(n))
ggplot(df_plot, aes(x = reorder(Origen, -n), y = n)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct,1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Número y % de valores faltantes (NA) en SO2 por estación",
x = "Estación (Origen)",
y = "Número de NAs"
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1)
)
Vemos que la gran mayoría de datos faltantes estan presentes en Torrent, ahora vamos a ver el porcentaje de faltantes que hay ahora.
resumen_total_so2 <- df %>%
filter(Origen %in% estaciones_so2) %>%
summarise(
total_obs = n(),
faltantes = sum(is.na(SO2)),
pct_faltantes = faltantes / total_obs * 100
)
print(resumen_total_so2)
## total_obs faltantes pct_faltantes
## 1 496525 14018 2.823221
Al tener en cuenta solo las estaciones que miden SO2 vemos que hay tan solo un 2.8% de faltantes.
Vamos a ver si los faltantes por año son iguales:
df_plot <- df %>%
filter(
Origen %in% estaciones_so2,
is.na(CO)
) %>%
count(Año, name = "n_missing_SO2") %>%
ungroup() %>%
mutate(
total_missing_SO2 = sum(n_missing_SO2),
pct_total = n_missing_SO2 / total_missing_SO2 * 100
) %>%
arrange(Año)
ggplot(df_plot, aes(x = factor(Año), y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de SO2 por año",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese año",
x = "Año",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
Vemos que cada año hay mas o menos la misma cantidad de faltantes. Lo cual nos indica que el año no esta muy relacionado con la cantidad de faltantes de SO2.
Ahora vamos a ver su distribución por meses.
df_plot <- df %>%
filter(Origen %in% estaciones_so2) %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
filter(is.na(SO2)) %>%
count(Mes, name = "n_missing") %>%
ungroup() %>%
mutate(
total_missing = sum(n_missing),
pct_total = n_missing / total_missing * 100
)
ggplot(df_plot, aes(x = Mes, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de SO2 por mes",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese mes",
x = "Mes",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5))
En el caso de los meses, la distribucion de los faltantes es parecida a los contaminantes PM10 y PM2.5, en este caso estando un poco mas distribuidos.
Ahora vamos a hacer lo mismo para dias de la semana, que en PM10 y PM2.5 estaban más o menos uniformemente distribuidos.
df_plot <- df %>%
filter(
Origen %in% estaciones_so2,
is.na(SO2)
) %>%
count(Dia_Semana, name = "n_missing") %>%
ungroup() %>%
mutate(
total_missing = sum(n_missing),
pct_total = n_missing / total_missing * 100
) %>%
mutate(Dia_Semana = factor(Dia_Semana, levels = dias_orden)) %>%
arrange(Dia_Semana)
ggplot(df_plot, aes(x = Dia_Semana, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de SO2 por día de la semana",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese día",
x = "Día de la semana",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle= element_text(hjust = 0.5)
)
En este caso, encontramos una distribución algo uniforme, teniendo más concentración a principio de la semana.
Por último veamos si por horas tienen una distribución similar, en PM10y PM2.5 era uniforme.
df_plot <- df %>%
filter(
Origen %in% estaciones_so2,
is.na(SO2)
) %>%
count(Hora, name = "n_missing") %>%
ungroup() %>%
mutate(
total_missing = sum(n_missing),
pct_total = n_missing / total_missing * 100 #
) %>%
arrange(Hora)
ggplot(df_plot, aes(x = Hora, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
scale_x_continuous(breaks = 0:23) +
labs(
title = "Porcentaje de valores faltantes de SO2 por hora del día",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en esa hora",
x = "Hora",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle= element_text(hjust = 0.5)
)
Como esperábamos, tienen una distribución muy similar. El hecho de que tengan muchas similitudes en las distribuciones de los faltantes puede indicar un patrón.
Para si hay un patrón vamos a ver si un faltante se repite en otros años. Esto lo vamos a hacer siguiendo estos pasos:
-filtrar estaciones que miden la variable - Seleccionar filas donde la variable es NA - Agrupar por combinación de Mes, Dia y Hora - Calcular el numero de años distintos con al menos ese NA y contruimos la lista de años - Solo nos quedamos con los que se repiten en más de un año
df_faltantes_so2 <- df %>%
filter(Origen %in% estaciones_so2)
faltantes_recurrentes_so2 <- df_faltantes_so2 %>%
filter(is.na(SO2)) %>%
group_by(Mes, Dia, Hora) %>%
summarise(
n_años = n_distinct(Año),
Años_con_faltante = paste(sort(unique(Año)), collapse = ", "),
.groups = "drop"
) %>%
filter(n_años > 1) %>%
select(Mes, Dia, Hora, Años_con_faltante)
print(faltantes_recurrentes_so2)
## # A tibble: 4,205 × 4
## Mes Dia Hora Años_con_faltante
## <chr> <int> <int> <chr>
## 1 Abril 2 2 2019, 2023
## 2 Abril 2 3 2019, 2023
## 3 Abril 2 4 2019, 2023
## 4 Abril 2 5 2019, 2023
## 5 Abril 2 6 2019, 2023
## 6 Abril 2 7 2019, 2023
## 7 Abril 2 8 2019, 2023
## 8 Abril 2 9 2019, 2023
## 9 Abril 2 10 2019, 2023
## 10 Abril 2 11 2019, 2023
## # ℹ 4,195 more rows
Podemos observar que hay fechas y horas con faltantes que se repiten en más de un año, y algunas fechas son las mismas que en PM10 y PM2.5, por lo que parece haber un patrón.
Vamos a visualizarlo de una forma más visual:
df %>%
mutate(missing = is.na(SO2)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c('TRUE' = "red", 'FALSE' = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de SO2",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
df %>%
filter(Origen %in% estaciones_so2) %>%
mutate(missing = is.na(SO2)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c('TRUE' = "red", 'FALSE' = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de SO2",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
Con esta gráfica podemos ver realmente los patrones, por ejemplo vemos al igual que en la tabla de antes que se repiten los faltanntes varios años.
Primero de todo vamos a ver la distribución de los datos de PM10.
hist(df$CO, breaks = 30, col = "skyblue", main = "Histograma de CO", xlab = "CO (µg/m³)")
Se observa una distribución sesgada a la derecha, lo cual podría deberse a la presencia de datos atípicos o outliers, que son valores que se desvían significativamente de la tendencia general de los datos. Para confirmar esto, vamos a calcular algunos estadísticos descriptivos.
summary(df$CO)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.00 0.10 0.10 0.14 0.20 2.30 395270
Podemos observar que el valor máximo de la variable es 2.3, lo cual es muchísimo más alto que la media y mediana. Vamos a ver a de donde vienen esos valores tan elevados. Para ello, vamos a ver la distribución del contaminante a lo largo del tiempo.
ggplot(df, aes(x = FechaHora, y = CO, group = 1)) +
geom_line(color = "steelblue") +
scale_x_datetime(
date_breaks = "1 year",
date_labels = "%Y"
) +
labs(
title = "Evolución temporal de CO",
x = "Año",
y = expression(CO~"(µg/m"^3*")")
) +
theme_minimal()
## Warning: Removed 5 rows containing missing values or values outside the scale range
## (`geom_line()`).
Parece se que la variable no se ve afectada por la DANA. Además, se ve un calro patrón con subidas y bajadas. Esto nos hace pensar que estará muy relacionada con temperatura.
Vamos a aplicar una transformación logarítmica para intentar normalizar la distribución.
df$log_CO <- log(df$CO + 1)
ggplot(df, aes(x = log_CO)) +
geom_histogram(
bins = 30,
fill = "lightblue",
color = "white"
) +
labs(
title = "Histograma de log(CO)",
x = expression(log(CO~"(µg/"*m^3*")")),
y = "Frecuencia"
) +
theme_minimal()
## Warning: Removed 395270 rows containing non-finite outside the scale range
## (`stat_bin()`).
Al aplicar la transformación logaritmica nos queda una distribucion unimidal con fuerte sesgo a la derecha. Como tiene una dustribución parecida a la de SO2, vamos a comprobar su homogeniedad.
Test de Levene
leveneTest(log_CO ~ Origen, data = df)
## Warning in leveneTest.default(y = y, group = group, ...): group coerced to
## factor.
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 10 1233.4 < 2.2e-16 ***
## 273466
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Dado que el p-value es < 0.05, rechazamos hipotesis nula, y por tanto afirmamos heterogeniedad. Como no hay ni normalidad ni heterocedasticidad, no podemos realizar ANOVA.
Vamos a ver la distribucion de la variable por cada zona.
df %>%
filter(!is.na(CO)) %>%
ggplot(aes(x = CO)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de CO por estación",
x = "CO (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
Todas las zonas tienen una distribucion similar, solo que algunas tienen valores más altos que cambian un poco la distribucion. Vamos a ver como son las logaritmicas.
df %>%
filter(!is.na(log_CO)) %>%
ggplot(aes(x = log_CO)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de log(CO) por estación",
x = "log(CO) (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
Sucede lo mismo que en CO. Algunas zonas si parecen diferir del resto, esto lo veremos más adelante.
Vamos a ver gráficamente las medias de CO para cada estación para asi ver si varia mucho los valores.
no_tomar_co <- c("València Olivereta",
"València - Centre",
"València - Nazaret Met-2",
"Benetusser UM",
"València-Conselleria Meteo",
"València - Vivers",
"València - Politècnic",
"València - Bulevard Sud",
"Quart de Poblet")
estaciones_co <- setdiff(unique(df$Origen), no_tomar_co)
df_co <- df %>%
filter(Origen %in% estaciones_co)
media_por_estacion <- df_co %>%
group_by(Origen) %>%
summarise(
media_CO = mean(CO, na.rm = TRUE),
n_obs = sum(!is.na(CO))
) %>%
ungroup() %>%
arrange(desc(media_CO)) %>%
mutate(Origen = factor(Origen, levels = Origen))
ggplot(media_por_estacion, aes(x = Origen, y = media_CO)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(media_CO, 2)),
vjust = -0.5, size = 3) +
labs(
title = "Media de CO por estación",
subtitle = "Solo estaciones que miden CO",
x = "Estación",
y = expression(Media~CO~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(media_por_estacion$Origen, "=", media_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Observamos que las medias varian entre 0.1 y 0.17 en todas las estaciones. Parece ser que todas las estaciones tienen valores similares pero hay que tener en cuenta que sus valores son muy pequeños.
Vamos a ver que sucede con las medianas.
df_co <- df %>%
filter(Origen %in% estaciones_co)
mediana_por_estacion <- df_co %>%
group_by(Origen) %>%
summarise(
mediana_CO = median(CO, na.rm = TRUE),
n_obs = sum(!is.na(CO))
) %>%
ungroup() %>%
arrange(desc(mediana_CO)) %>%
mutate(Origen = factor(Origen, levels = Origen))
ggplot(mediana_por_estacion, aes(x = Origen, y = mediana_CO)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(mediana_CO, 2)),
vjust = -0.5, size = 3) +
labs(
title = "Mediana de CO por estación",
subtitle = "Solo estaciones que miden CO",
x = "Estación",
y = expression(Mediana~CO~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(mediana_por_estacion$Origen, "=", mediana_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Parecido a lo sucedido con SO2, observamos que todas las estaciones tienen la misma mediana. Esto se debe a la distribución unimodal. Vamos a ver su desviación típica.
df_co <- df %>%
filter(Origen %in% estaciones_co)
sd_por_estacion <- df_co %>%
group_by(Origen) %>%
summarise(
sd_CO = sd(CO, na.rm = TRUE),
n_obs = sum(!is.na(CO))
) %>%
ungroup() %>%
arrange(desc(sd_CO)) %>%
mutate(Origen = factor(Origen, levels = Origen))
ggplot(sd_por_estacion, aes(x = Origen, y = sd_CO)) +
geom_col(fill = "steelblue") +
geom_text(aes(label = round(sd_CO, 2)),
vjust = -0.5, size = 3) +
labs(
title = "Desviación típica de CO por estación",
subtitle = "Solo estaciones que miden CO",
x = "Estación",
y = expression(SD~CO~"(µg/m³)"),
caption = paste0("Número de observaciones válidas por estación:\n",
paste(mediana_por_estacion$Origen, "=", mediana_por_estacion$n_obs, collapse = "; "))
) +
theme_minimal(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5)
)
Observamos que Paiporta tiene una variabilidad de 0, lo que probablemente se deba a un error, o que simplemente no hay muchos datos. Además se observa una diferencia muy grande entre las esatciones con mucha variabilidad y las que tienen pocas. Al igual que en SO2, son las estaciones cerca del puerto, lo que se puede justificar con la actividad portuaria.
df_paiporta_co <- df %>%
filter(Origen == "Paiporta UM") %>%
select(FechaHora, CO)
df_paiporta_co
## FechaHora CO
## 1 2024-11-25 17:00:00 0.1
## 2 2024-11-25 18:00:00 0.1
## 3 2024-11-25 19:00:00 0.1
## 4 2024-11-25 20:00:00 0.1
## 5 2024-11-25 21:00:00 0.1
## 6 2024-11-25 22:00:00 0.1
## 7 2024-11-25 23:00:00 0.1
## 8 2024-11-26 00:00:00 0.1
## 9 2024-11-26 01:00:00 0.1
## 10 2024-11-26 02:00:00 0.1
## 11 2024-11-26 03:00:00 0.1
## 12 2024-11-26 04:00:00 0.1
## 13 2024-11-26 05:00:00 0.1
## 14 2024-11-26 06:00:00 0.1
## 15 2024-11-26 07:00:00 0.1
## 16 2024-11-26 08:00:00 0.1
## 17 2024-11-26 09:00:00 0.1
## 18 2024-11-26 10:00:00 0.1
## 19 2024-11-26 11:00:00 0.1
## 20 2024-11-26 12:00:00 0.1
## 21 2024-11-26 13:00:00 0.1
## 22 2024-11-26 14:00:00 0.1
## 23 2024-11-26 15:00:00 0.1
## 24 2024-11-26 16:00:00 0.1
## 25 2024-11-26 17:00:00 0.1
## 26 2024-11-26 18:00:00 0.1
## 27 2024-11-26 19:00:00 0.1
## 28 2024-11-26 20:00:00 0.1
## 29 2024-11-26 21:00:00 0.1
## 30 2024-11-26 22:00:00 0.1
## 31 2024-11-26 23:00:00 0.1
## 32 2024-11-27 00:00:00 0.1
## 33 2024-11-27 01:00:00 0.1
## 34 2024-11-27 02:00:00 0.1
## 35 2024-11-27 03:00:00 0.1
## 36 2024-11-27 04:00:00 0.1
## 37 2024-11-27 05:00:00 0.1
## 38 2024-11-27 06:00:00 0.1
## 39 2024-11-27 07:00:00 0.1
## 40 2024-11-27 08:00:00 0.1
## 41 2024-11-27 09:00:00 0.1
## 42 2024-11-27 10:00:00 0.1
## 43 2024-11-27 11:00:00 0.1
## 44 2024-11-27 12:00:00 0.1
## 45 2024-11-27 13:00:00 0.1
## 46 2024-11-27 14:00:00 0.1
## 47 2024-11-27 15:00:00 0.1
## 48 2024-11-27 16:00:00 0.1
## 49 2024-11-27 17:00:00 0.1
## 50 2024-11-27 18:00:00 0.1
## 51 2024-11-27 19:00:00 0.1
## 52 2024-11-27 21:00:00 0.1
## 53 2024-11-27 22:00:00 0.1
## 54 2024-11-27 23:00:00 0.1
## 55 2024-11-28 00:00:00 0.1
## 56 2024-11-28 01:00:00 0.1
## 57 2024-11-28 02:00:00 0.1
## 58 2024-11-28 03:00:00 0.1
## 59 2024-11-28 04:00:00 0.1
## 60 2024-11-28 05:00:00 0.1
## 61 2024-11-28 06:00:00 0.1
## 62 2024-11-28 07:00:00 0.1
## 63 2024-11-28 08:00:00 0.1
## 64 2024-11-28 09:00:00 0.1
## 65 2024-11-28 10:00:00 0.1
## 66 2024-11-28 11:00:00 0.1
## 67 2024-11-28 12:00:00 0.1
## 68 2024-11-28 13:00:00 0.1
## 69 2024-11-28 14:00:00 0.1
## 70 2024-11-28 15:00:00 0.1
## 71 2024-11-28 16:00:00 0.1
## 72 2024-11-28 17:00:00 0.1
## 73 2024-11-28 18:00:00 0.1
## 74 2024-11-28 19:00:00 0.1
## 75 2024-11-28 20:00:00 0.1
## 76 2024-11-28 21:00:00 0.1
## 77 2024-11-28 22:00:00 0.1
## 78 2024-11-28 23:00:00 0.1
## 79 2024-11-29 00:00:00 0.1
## 80 2024-11-29 01:00:00 0.1
## 81 2024-11-29 02:00:00 0.1
## 82 2024-11-29 03:00:00 0.1
## 83 2024-11-29 04:00:00 0.1
## 84 2024-11-29 05:00:00 0.1
## 85 2024-11-29 06:00:00 0.1
## 86 2024-11-29 07:00:00 0.1
## 87 2024-11-29 08:00:00 0.1
## 88 2024-11-29 09:00:00 0.1
## 89 2024-11-29 10:00:00 0.1
## 90 2024-11-29 11:00:00 0.1
## 91 2024-11-29 12:00:00 0.1
## 92 2024-11-29 13:00:00 0.1
## 93 2024-11-29 14:00:00 0.1
## 94 2024-11-29 15:00:00 0.1
## 95 2024-11-29 16:00:00 0.1
## 96 2024-11-29 18:00:00 0.1
## 97 2024-11-29 19:00:00 0.1
## 98 2024-11-29 20:00:00 0.1
## 99 2024-11-29 21:00:00 0.1
## 100 2024-11-29 22:00:00 0.1
## 101 2024-11-29 23:00:00 0.1
## 102 2024-11-30 00:00:00 0.1
## 103 2024-11-30 01:00:00 0.1
## 104 2024-11-30 02:00:00 0.1
## 105 2024-11-30 03:00:00 0.1
## 106 2024-11-30 04:00:00 0.1
## 107 2024-11-30 05:00:00 0.1
## 108 2024-11-30 06:00:00 0.1
## 109 2024-11-30 07:00:00 0.1
## 110 2024-11-30 08:00:00 0.1
## 111 2024-11-30 09:00:00 0.1
## 112 2024-11-30 10:00:00 0.1
## 113 2024-11-30 11:00:00 0.1
## 114 2024-11-30 12:00:00 0.1
## 115 2024-11-30 13:00:00 0.1
## 116 2024-11-30 14:00:00 0.1
## 117 2024-11-30 15:00:00 0.1
## 118 2024-11-30 16:00:00 0.1
## 119 2024-11-30 17:00:00 0.1
## 120 2024-11-30 18:00:00 0.1
## 121 2024-11-30 19:00:00 0.1
## 122 2024-11-30 20:00:00 0.1
## 123 2024-11-30 21:00:00 0.1
## 124 2024-11-30 22:00:00 0.1
## 125 2024-11-30 23:00:00 0.1
## 126 2024-12-01 00:00:00 0.1
## 127 2024-12-01 01:00:00 0.1
## 128 2024-12-01 02:00:00 0.1
## 129 2024-12-01 03:00:00 0.1
## 130 2024-12-01 04:00:00 0.1
## 131 2024-12-01 05:00:00 0.1
## 132 2024-12-01 06:00:00 0.1
## 133 2024-12-01 07:00:00 0.1
## 134 2024-12-01 08:00:00 0.1
## 135 2024-12-01 09:00:00 0.1
## 136 2024-12-01 10:00:00 0.1
## 137 2024-12-01 11:00:00 0.1
## 138 2024-12-01 12:00:00 0.1
## 139 2024-12-01 13:00:00 0.1
## 140 2024-12-01 14:00:00 0.1
## 141 2024-12-01 15:00:00 0.1
## 142 2024-12-01 16:00:00 0.1
## 143 2024-12-01 17:00:00 0.1
## 144 2024-12-01 18:00:00 0.1
## 145 2024-12-01 19:00:00 0.1
## 146 2024-12-01 20:00:00 0.1
## 147 2024-12-01 21:00:00 0.1
## 148 2024-12-01 22:00:00 0.1
## 149 2024-12-01 23:00:00 0.1
## 150 2024-12-02 00:00:00 0.1
## 151 2024-12-02 01:00:00 0.1
## 152 2024-12-02 02:00:00 0.1
## 153 2024-12-02 03:00:00 0.1
## 154 2024-12-02 04:00:00 0.1
## 155 2024-12-02 05:00:00 0.1
## 156 2024-12-02 06:00:00 0.1
## 157 2024-12-02 07:00:00 0.1
## 158 2024-12-02 08:00:00 0.1
## 159 2024-12-02 09:00:00 0.1
## 160 2024-12-02 10:00:00 0.1
## 161 2024-12-02 11:00:00 0.1
## 162 2024-12-02 12:00:00 0.1
## 163 2024-12-02 13:00:00 0.1
## 164 2024-12-02 14:00:00 0.1
## 165 2024-12-02 15:00:00 0.1
## 166 2024-12-02 16:00:00 0.1
## 167 2024-12-02 17:00:00 0.1
## 168 2024-12-02 18:00:00 0.1
## 169 2024-12-02 19:00:00 0.1
## 170 2024-12-02 20:00:00 0.1
## 171 2024-12-02 21:00:00 0.1
## 172 2024-12-02 22:00:00 0.1
## 173 2024-12-02 23:00:00 0.1
## 174 2024-12-03 00:00:00 0.1
## 175 2024-12-03 01:00:00 0.1
## 176 2024-12-03 02:00:00 0.1
## 177 2024-12-03 03:00:00 0.1
## 178 2024-12-03 04:00:00 0.1
## 179 2024-12-03 05:00:00 0.1
## 180 2024-12-03 06:00:00 0.1
## 181 2024-12-03 07:00:00 0.1
## 182 2024-12-03 08:00:00 0.1
## 183 2024-12-03 09:00:00 0.1
## 184 2024-12-03 10:00:00 0.1
## 185 2024-12-03 11:00:00 0.1
## 186 2024-12-03 12:00:00 0.1
## 187 2024-12-03 13:00:00 0.1
## 188 2024-12-03 14:00:00 0.1
## 189 2024-12-03 15:00:00 0.1
## 190 2024-12-03 16:00:00 0.1
## 191 2024-12-03 17:00:00 0.1
## 192 2024-12-03 18:00:00 0.1
## 193 2024-12-03 19:00:00 0.1
## 194 2024-12-03 20:00:00 0.1
## 195 2024-12-03 21:00:00 0.1
## 196 2024-12-03 22:00:00 0.1
## 197 2024-12-03 23:00:00 0.1
## 198 2024-12-04 00:00:00 0.1
## 199 2024-12-04 01:00:00 0.1
## 200 2024-12-04 02:00:00 0.1
## 201 2024-12-04 03:00:00 0.1
## 202 2024-12-04 04:00:00 0.1
## 203 2024-12-04 05:00:00 0.1
## 204 2024-12-04 06:00:00 0.1
## 205 2024-12-04 07:00:00 0.1
## 206 2024-12-04 08:00:00 0.1
## 207 2024-12-04 09:00:00 0.1
## 208 2024-12-04 11:00:00 0.1
## 209 2024-12-04 12:00:00 0.1
## 210 2024-12-04 13:00:00 0.1
## 211 2024-12-04 14:00:00 0.1
## 212 2024-12-04 15:00:00 0.1
## 213 2024-12-04 16:00:00 0.1
## 214 2024-12-04 17:00:00 0.1
## 215 2024-12-04 18:00:00 0.1
## 216 2024-12-04 19:00:00 0.1
## 217 2024-12-04 20:00:00 0.1
## 218 2024-12-04 21:00:00 0.1
## 219 2024-12-04 22:00:00 0.1
## 220 2024-12-04 23:00:00 0.1
## 221 2024-12-05 00:00:00 0.1
## 222 2024-12-05 01:00:00 0.1
## 223 2024-12-05 02:00:00 0.1
## 224 2024-12-05 03:00:00 0.1
## 225 2024-12-05 04:00:00 0.1
## 226 2024-12-05 05:00:00 0.1
## 227 2024-12-05 06:00:00 0.1
## 228 2024-12-05 07:00:00 0.1
## 229 2024-12-05 08:00:00 0.1
## 230 2024-12-05 10:00:00 0.1
## 231 2024-12-05 11:00:00 0.1
## 232 2024-12-05 12:00:00 0.1
## 233 2024-12-05 13:00:00 0.1
## 234 2024-12-05 14:00:00 0.1
## 235 2024-12-05 15:00:00 0.1
## 236 2024-12-05 16:00:00 0.1
## 237 2024-12-05 17:00:00 0.1
## 238 2024-12-05 18:00:00 0.1
## 239 2024-12-05 19:00:00 0.1
## 240 2024-12-05 20:00:00 0.1
## 241 2024-12-05 21:00:00 0.1
## 242 2024-12-05 22:00:00 0.1
## 243 2024-12-05 23:00:00 0.1
## 244 2024-12-06 00:00:00 0.1
## 245 2024-12-06 01:00:00 0.1
## 246 2024-12-06 02:00:00 0.1
## 247 2024-12-06 03:00:00 0.1
## 248 2024-12-06 04:00:00 0.1
## 249 2024-12-06 05:00:00 0.1
## 250 2024-12-06 06:00:00 0.1
## 251 2024-12-06 07:00:00 0.1
## 252 2024-12-06 08:00:00 0.1
## 253 2024-12-06 09:00:00 0.1
## 254 2024-12-06 10:00:00 0.1
## 255 2024-12-06 11:00:00 0.1
## 256 2024-12-06 12:00:00 0.1
## 257 2024-12-06 13:00:00 0.1
## 258 2024-12-06 14:00:00 0.1
## 259 2024-12-06 15:00:00 0.1
## 260 2024-12-06 16:00:00 0.1
## 261 2024-12-06 17:00:00 0.1
## 262 2024-12-06 18:00:00 0.1
## 263 2024-12-06 19:00:00 0.1
## 264 2024-12-06 20:00:00 0.1
## 265 2024-12-06 21:00:00 0.1
## 266 2024-12-06 22:00:00 0.1
## 267 2024-12-06 23:00:00 0.1
## 268 2024-12-07 00:00:00 0.1
## 269 2024-12-07 01:00:00 0.1
## 270 2024-12-07 02:00:00 0.1
## 271 2024-12-07 03:00:00 0.1
## 272 2024-12-07 04:00:00 0.1
## 273 2024-12-07 05:00:00 0.1
## 274 2024-12-07 06:00:00 0.1
## 275 2024-12-07 07:00:00 0.1
## 276 2024-12-07 08:00:00 0.1
## 277 2024-12-07 09:00:00 0.1
## 278 2024-12-07 10:00:00 0.1
## 279 2024-12-07 11:00:00 0.1
## 280 2024-12-07 12:00:00 0.1
## 281 2024-12-07 13:00:00 0.1
## 282 2024-12-07 14:00:00 0.1
## 283 2024-12-07 15:00:00 0.1
## 284 2024-12-07 16:00:00 0.1
## 285 2024-12-07 17:00:00 0.1
## 286 2024-12-07 18:00:00 0.1
## 287 2024-12-07 19:00:00 0.1
## 288 2024-12-07 20:00:00 0.1
## 289 2024-12-07 21:00:00 0.1
## 290 2024-12-07 22:00:00 0.1
## 291 2024-12-07 23:00:00 0.1
## 292 2024-12-08 00:00:00 0.1
## 293 2024-12-08 01:00:00 0.1
## 294 2024-12-08 02:00:00 0.1
## 295 2024-12-08 03:00:00 0.1
## 296 2024-12-08 04:00:00 0.1
## 297 2024-12-08 05:00:00 0.1
## 298 2024-12-08 06:00:00 0.1
## 299 2024-12-08 07:00:00 0.1
## 300 2024-12-08 08:00:00 0.1
## 301 2024-12-08 09:00:00 0.1
## 302 2024-12-08 10:00:00 0.1
## 303 2024-12-08 11:00:00 0.1
## 304 2024-12-08 12:00:00 0.1
## 305 2024-12-08 13:00:00 0.1
## 306 2024-12-08 14:00:00 0.1
## 307 2024-12-08 15:00:00 0.1
## 308 2024-12-08 16:00:00 0.1
## 309 2024-12-08 17:00:00 0.1
## 310 2024-12-08 18:00:00 0.1
## 311 2024-12-08 19:00:00 0.1
## 312 2024-12-08 20:00:00 0.1
## 313 2024-12-08 21:00:00 0.1
## 314 2024-12-08 22:00:00 0.1
## 315 2024-12-08 23:00:00 0.1
## 316 2024-12-09 00:00:00 0.1
## 317 2024-12-09 01:00:00 0.1
## 318 2024-12-09 02:00:00 0.1
## 319 2024-12-09 03:00:00 0.1
## 320 2024-12-09 04:00:00 0.1
## 321 2024-12-09 05:00:00 0.1
## 322 2024-12-09 06:00:00 0.1
## 323 2024-12-09 07:00:00 0.1
## 324 2024-12-09 08:00:00 0.1
## 325 2024-12-09 09:00:00 0.1
## 326 2024-12-09 10:00:00 0.1
## 327 2024-12-09 11:00:00 0.1
## 328 2024-12-09 12:00:00 0.1
## 329 2024-12-09 13:00:00 0.1
## 330 2024-12-09 14:00:00 0.1
## 331 2024-12-09 15:00:00 0.1
## 332 2024-12-09 16:00:00 0.1
## 333 2024-12-09 17:00:00 0.1
## 334 2024-12-09 18:00:00 0.1
## 335 2024-12-09 19:00:00 0.1
## 336 2024-12-09 20:00:00 0.1
## 337 2024-12-09 21:00:00 0.1
## 338 2024-12-09 22:00:00 0.1
## 339 2024-12-09 23:00:00 0.1
## 340 2024-12-10 00:00:00 0.1
## 341 2024-12-10 01:00:00 0.1
## 342 2024-12-10 02:00:00 0.1
## 343 2024-12-10 03:00:00 0.1
## 344 2024-12-10 04:00:00 0.1
## 345 2024-12-10 05:00:00 0.1
## 346 2024-12-10 06:00:00 0.1
## 347 2024-12-10 07:00:00 0.1
## 348 2024-12-10 08:00:00 0.1
## 349 2024-12-10 09:00:00 0.1
## 350 2024-12-10 10:00:00 0.1
## 351 2024-12-10 11:00:00 0.1
## 352 2024-12-10 12:00:00 0.1
## 353 2024-12-10 13:00:00 0.1
## 354 2024-12-10 14:00:00 0.1
## 355 2024-12-10 15:00:00 0.1
## 356 2024-12-10 16:00:00 0.1
## 357 2024-12-10 17:00:00 0.1
## 358 2024-12-10 18:00:00 0.1
## 359 2024-12-10 19:00:00 0.1
## 360 2024-12-10 20:00:00 0.1
## 361 2024-12-10 21:00:00 0.1
## 362 2024-12-10 22:00:00 0.1
## 363 2024-12-10 23:00:00 0.1
## 364 2024-12-11 00:00:00 0.1
## 365 2024-12-11 01:00:00 0.1
## 366 2024-12-11 02:00:00 0.1
## 367 2024-12-11 03:00:00 0.1
## 368 2024-12-11 04:00:00 0.1
## 369 2024-12-11 05:00:00 0.1
## 370 2024-12-11 06:00:00 0.1
## 371 2024-12-11 07:00:00 0.1
## 372 2024-12-11 08:00:00 0.1
## 373 2024-12-11 09:00:00 0.1
## 374 2024-12-11 11:00:00 0.1
## 375 2024-12-11 12:00:00 0.1
## 376 2024-12-11 13:00:00 0.1
## 377 2024-12-11 14:00:00 0.1
## 378 2024-12-11 15:00:00 0.1
## 379 2024-12-11 16:00:00 0.1
## 380 2024-12-11 17:00:00 0.1
## 381 2024-12-11 18:00:00 0.1
## 382 2024-12-11 19:00:00 0.1
## 383 2024-12-11 20:00:00 0.1
## 384 2024-12-11 21:00:00 0.1
## 385 2024-12-11 22:00:00 0.1
## 386 2024-12-11 23:00:00 0.1
## 387 2024-12-12 00:00:00 0.1
## 388 2024-12-12 01:00:00 0.1
## 389 2024-12-12 02:00:00 0.1
## 390 2024-12-12 03:00:00 0.1
## 391 2024-12-12 04:00:00 0.1
## 392 2024-12-12 05:00:00 0.1
## 393 2024-12-12 06:00:00 0.1
## 394 2024-12-12 07:00:00 0.1
## 395 2024-12-12 08:00:00 0.1
## 396 2024-12-12 09:00:00 NA
## 397 2024-12-12 10:00:00 0.1
## 398 2024-12-12 11:00:00 0.1
## 399 2024-12-12 12:00:00 0.1
## 400 2024-12-12 13:00:00 0.1
## 401 2024-12-12 14:00:00 0.1
## 402 2024-12-12 15:00:00 0.1
## 403 2024-12-12 16:00:00 0.1
## 404 2024-12-12 17:00:00 0.1
## 405 2024-12-12 18:00:00 0.1
## 406 2024-12-12 19:00:00 0.1
## 407 2024-12-12 20:00:00 0.1
## 408 2024-12-12 21:00:00 0.1
## 409 2024-12-12 22:00:00 0.1
## 410 2024-12-12 23:00:00 0.1
## 411 2024-12-13 00:00:00 0.1
## 412 2024-12-13 01:00:00 0.1
## 413 2024-12-13 02:00:00 0.1
## 414 2024-12-13 03:00:00 0.1
## 415 2024-12-13 04:00:00 0.1
## 416 2024-12-13 05:00:00 0.1
## 417 2024-12-13 06:00:00 0.1
## 418 2024-12-13 07:00:00 0.1
## 419 2024-12-13 08:00:00 0.1
## 420 2024-12-13 09:00:00 0.1
## 421 2024-12-13 10:00:00 0.1
## 422 2024-12-13 11:00:00 0.1
## 423 2024-12-13 12:00:00 0.1
## 424 2024-12-13 13:00:00 0.1
## 425 2024-12-13 14:00:00 0.1
## 426 2024-12-13 15:00:00 0.1
## 427 2024-12-13 16:00:00 0.1
## 428 2024-12-13 17:00:00 0.1
## 429 2024-12-13 18:00:00 0.1
## 430 2024-12-13 19:00:00 0.1
## 431 2024-12-13 20:00:00 0.1
## 432 2024-12-13 21:00:00 0.1
## 433 2024-12-13 22:00:00 0.1
## 434 2024-12-13 23:00:00 0.1
## 435 2024-12-14 00:00:00 0.1
## 436 2024-12-14 01:00:00 0.1
## 437 2024-12-14 02:00:00 0.1
## 438 2024-12-14 03:00:00 0.1
## 439 2024-12-14 04:00:00 0.1
## 440 2024-12-14 05:00:00 0.1
## 441 2024-12-14 06:00:00 0.1
## 442 2024-12-14 07:00:00 0.1
## 443 2024-12-14 08:00:00 0.1
## 444 2024-12-14 09:00:00 0.1
## 445 2024-12-14 10:00:00 0.1
## 446 2024-12-14 11:00:00 0.1
## 447 2024-12-14 12:00:00 0.1
## 448 2024-12-14 13:00:00 0.1
## 449 2024-12-14 14:00:00 0.1
## 450 2024-12-14 15:00:00 0.1
## 451 2024-12-14 16:00:00 0.1
## 452 2024-12-14 17:00:00 0.1
## 453 2024-12-14 18:00:00 0.1
## 454 2024-12-14 19:00:00 0.1
## 455 2024-12-14 20:00:00 0.1
## 456 2024-12-14 21:00:00 0.1
## 457 2024-12-14 22:00:00 0.1
## 458 2024-12-14 23:00:00 0.1
## 459 2024-12-15 00:00:00 0.1
## 460 2024-12-15 01:00:00 0.1
## 461 2024-12-15 02:00:00 0.1
## 462 2024-12-15 03:00:00 0.1
## 463 2024-12-15 04:00:00 0.1
## 464 2024-12-15 05:00:00 0.1
## 465 2024-12-15 06:00:00 0.1
## 466 2024-12-15 07:00:00 0.1
## 467 2024-12-15 08:00:00 0.1
## 468 2024-12-15 09:00:00 0.1
## 469 2024-12-15 10:00:00 0.1
## 470 2024-12-15 11:00:00 0.1
## 471 2024-12-15 12:00:00 0.1
## 472 2024-12-15 13:00:00 0.1
## 473 2024-12-15 14:00:00 0.1
## 474 2024-12-15 15:00:00 0.1
## 475 2024-12-15 16:00:00 0.1
## 476 2024-12-15 17:00:00 0.1
## 477 2024-12-15 18:00:00 0.1
## 478 2024-12-15 19:00:00 0.1
## 479 2024-12-15 20:00:00 0.1
## 480 2024-12-15 21:00:00 0.1
## 481 2024-12-15 22:00:00 0.1
## 482 2024-12-15 23:00:00 0.1
## 483 2024-12-16 00:00:00 0.1
## 484 2024-12-16 01:00:00 0.1
## 485 2024-12-16 02:00:00 0.1
## 486 2024-12-16 03:00:00 0.1
## 487 2024-12-16 04:00:00 0.1
## 488 2024-12-16 05:00:00 0.1
## 489 2024-12-16 08:00:00 0.1
## 490 2024-12-16 09:00:00 0.1
## 491 2024-12-16 10:00:00 0.1
## 492 2024-12-16 11:00:00 0.1
## 493 2024-12-16 12:00:00 0.1
## 494 2024-12-16 13:00:00 0.1
## 495 2024-12-16 14:00:00 0.1
## 496 2024-12-16 16:00:00 0.1
## 497 2024-12-16 17:00:00 0.1
## 498 2024-12-16 18:00:00 0.1
## 499 2024-12-16 19:00:00 0.1
## 500 2024-12-16 20:00:00 0.1
## 501 2024-12-16 21:00:00 0.1
## 502 2024-12-16 22:00:00 0.1
## 503 2024-12-16 23:00:00 0.1
## 504 2024-12-17 00:00:00 0.1
## 505 2024-12-17 01:00:00 0.1
## 506 2024-12-17 02:00:00 0.1
## 507 2024-12-17 03:00:00 0.1
## 508 2024-12-17 04:00:00 0.1
## 509 2024-12-17 05:00:00 0.1
## 510 2024-12-17 06:00:00 0.1
## 511 2024-12-17 07:00:00 0.1
## 512 2024-12-17 08:00:00 0.1
## 513 2024-12-17 09:00:00 0.1
## 514 2024-12-17 10:00:00 0.1
## 515 2024-12-17 11:00:00 0.1
## 516 2024-12-17 12:00:00 0.1
## 517 2024-12-17 13:00:00 0.1
## 518 2024-12-17 14:00:00 0.1
## 519 2024-12-17 17:00:00 0.1
## 520 2024-12-17 18:00:00 0.1
## 521 2024-12-17 19:00:00 0.1
## 522 2024-12-17 20:00:00 0.1
## 523 2024-12-17 21:00:00 0.1
## 524 2024-12-17 22:00:00 0.1
## 525 2024-12-17 23:00:00 0.1
## 526 2024-12-18 00:00:00 0.1
## 527 2024-12-18 01:00:00 0.1
## 528 2024-12-18 02:00:00 0.1
## 529 2024-12-18 03:00:00 0.1
## 530 2024-12-18 04:00:00 0.1
## 531 2024-12-18 05:00:00 0.1
## 532 2024-12-18 06:00:00 0.1
## 533 2024-12-18 07:00:00 0.1
## 534 2024-12-18 08:00:00 0.1
## 535 2024-12-18 09:00:00 0.1
## 536 2024-12-18 10:00:00 0.1
## 537 2024-12-18 11:00:00 0.1
## 538 2024-12-18 12:00:00 0.1
## 539 2024-12-18 13:00:00 0.1
## 540 2024-12-18 14:00:00 0.1
## 541 2024-12-18 16:00:00 0.1
## 542 2024-12-18 17:00:00 0.1
## 543 2024-12-18 18:00:00 0.1
## 544 2024-12-18 19:00:00 0.1
## 545 2024-12-18 20:00:00 0.1
## 546 2024-12-18 21:00:00 0.1
## 547 2024-12-18 22:00:00 0.1
## 548 2024-12-18 23:00:00 0.1
## 549 2024-12-19 00:00:00 0.1
## 550 2024-12-19 01:00:00 0.1
## 551 2024-12-19 02:00:00 0.1
## 552 2024-12-19 03:00:00 0.1
## 553 2024-12-19 04:00:00 0.1
## 554 2024-12-19 05:00:00 0.1
## 555 2024-12-19 06:00:00 0.1
## 556 2024-12-19 07:00:00 0.1
## 557 2024-12-19 08:00:00 0.1
## 558 2024-12-19 09:00:00 0.1
## 559 2024-12-19 10:00:00 0.1
## 560 2024-12-19 11:00:00 0.1
## 561 2024-12-19 12:00:00 0.1
## 562 2024-12-19 13:00:00 0.1
## 563 2024-12-19 14:00:00 0.1
## 564 2024-12-19 15:00:00 0.1
## 565 2024-12-19 16:00:00 0.1
## 566 2024-12-19 17:00:00 0.1
## 567 2024-12-19 18:00:00 0.1
## 568 2024-12-19 19:00:00 0.1
## 569 2024-12-19 20:00:00 0.1
## 570 2024-12-19 21:00:00 0.1
## 571 2024-12-19 22:00:00 0.1
## 572 2024-12-19 23:00:00 0.1
## 573 2024-12-20 00:00:00 0.1
## 574 2024-12-20 01:00:00 0.1
## 575 2024-12-20 02:00:00 0.1
## 576 2024-12-20 03:00:00 0.1
## 577 2024-12-20 04:00:00 0.1
## 578 2024-12-20 05:00:00 0.1
## 579 2024-12-20 06:00:00 0.1
## 580 2024-12-20 07:00:00 0.1
## 581 2024-12-20 08:00:00 0.1
## 582 2024-12-20 09:00:00 NA
## 583 2024-12-20 10:00:00 NA
## 584 2024-12-20 11:00:00 0.1
## 585 2024-12-20 12:00:00 0.1
## 586 2024-12-20 13:00:00 0.1
## 587 2024-12-20 14:00:00 0.1
## 588 2024-12-20 15:00:00 0.1
## 589 2024-12-20 16:00:00 0.1
## 590 2024-12-20 17:00:00 0.1
## 591 2024-12-20 18:00:00 0.1
## 592 2024-12-20 19:00:00 0.1
## 593 2024-12-20 20:00:00 0.1
## 594 2024-12-20 21:00:00 0.1
## 595 2024-12-20 22:00:00 0.1
## 596 2024-12-20 23:00:00 0.1
## 597 2024-12-21 00:00:00 0.1
## 598 2024-12-21 01:00:00 0.1
## 599 2024-12-21 02:00:00 0.1
## 600 2024-12-21 03:00:00 0.1
## 601 2024-12-21 04:00:00 0.1
## 602 2024-12-21 05:00:00 0.1
## 603 2024-12-21 06:00:00 0.1
## 604 2024-12-21 07:00:00 0.1
## 605 2024-12-21 08:00:00 0.1
## 606 2024-12-21 09:00:00 0.1
## 607 2024-12-21 10:00:00 0.1
## 608 2024-12-21 11:00:00 0.1
## 609 2024-12-21 12:00:00 0.1
## 610 2024-12-21 13:00:00 0.1
## 611 2024-12-21 14:00:00 0.1
## 612 2024-12-21 15:00:00 0.1
## 613 2024-12-21 16:00:00 0.1
## 614 2024-12-21 17:00:00 0.1
## 615 2024-12-21 18:00:00 0.1
## 616 2024-12-21 19:00:00 0.1
## 617 2024-12-21 20:00:00 0.1
## 618 2024-12-21 21:00:00 0.1
## 619 2024-12-21 22:00:00 0.1
## 620 2024-12-21 23:00:00 0.1
## 621 2024-12-22 00:00:00 0.1
## 622 2024-12-22 01:00:00 0.1
## 623 2024-12-22 02:00:00 0.1
## 624 2024-12-22 03:00:00 0.1
## 625 2024-12-22 04:00:00 0.1
## 626 2024-12-22 05:00:00 0.1
## 627 2024-12-22 06:00:00 0.1
## 628 2024-12-22 07:00:00 0.1
## 629 2024-12-22 08:00:00 0.1
## 630 2024-12-22 09:00:00 0.1
## 631 2024-12-22 10:00:00 0.1
## 632 2024-12-22 11:00:00 0.1
## 633 2024-12-22 12:00:00 0.1
## 634 2024-12-22 13:00:00 0.1
## 635 2024-12-22 14:00:00 0.1
## 636 2024-12-22 15:00:00 0.1
## 637 2024-12-22 16:00:00 0.1
## 638 2024-12-22 17:00:00 0.1
## 639 2024-12-22 18:00:00 0.1
## 640 2024-12-22 19:00:00 0.1
## 641 2024-12-22 20:00:00 0.1
## 642 2024-12-22 21:00:00 0.1
## 643 2024-12-22 22:00:00 0.1
## 644 2024-12-22 23:00:00 0.1
## 645 2024-12-23 00:00:00 0.1
## 646 2024-12-23 01:00:00 0.1
## 647 2024-12-23 02:00:00 0.1
## 648 2024-12-23 03:00:00 0.1
## 649 2024-12-23 04:00:00 0.1
## 650 2024-12-23 05:00:00 0.1
## 651 2024-12-23 06:00:00 0.1
## 652 2024-12-23 07:00:00 0.1
## 653 2024-12-23 08:00:00 0.1
## 654 2024-12-23 09:00:00 0.1
## 655 2024-12-23 10:00:00 0.1
## 656 2024-12-23 11:00:00 0.1
## 657 2024-12-23 12:00:00 0.1
## 658 2024-12-23 13:00:00 0.1
## 659 2024-12-23 14:00:00 0.1
## 660 2024-12-23 15:00:00 0.1
## 661 2024-12-23 16:00:00 0.1
## 662 2024-12-23 17:00:00 0.1
## 663 2024-12-23 18:00:00 0.1
## 664 2024-12-23 19:00:00 0.1
## 665 2024-12-23 20:00:00 0.1
## 666 2024-12-23 21:00:00 0.1
## 667 2024-12-23 22:00:00 0.1
## 668 2024-12-23 23:00:00 0.1
## 669 2024-12-24 00:00:00 0.1
## 670 2024-12-24 01:00:00 0.1
## 671 2024-12-24 02:00:00 0.1
## 672 2024-12-24 03:00:00 0.1
## 673 2024-12-24 04:00:00 0.1
## 674 2024-12-24 05:00:00 0.1
## 675 2024-12-24 06:00:00 0.1
## 676 2024-12-24 07:00:00 0.1
## 677 2024-12-24 08:00:00 0.1
## 678 2024-12-24 09:00:00 0.1
## 679 2024-12-24 10:00:00 0.1
## 680 2024-12-24 11:00:00 0.1
## 681 2024-12-24 12:00:00 0.1
## 682 2024-12-24 13:00:00 0.1
## 683 2024-12-24 14:00:00 0.1
## 684 2024-12-24 15:00:00 0.1
## 685 2024-12-24 16:00:00 0.1
## 686 2024-12-24 17:00:00 0.1
## 687 2024-12-24 18:00:00 0.1
## 688 2024-12-24 19:00:00 0.1
## 689 2024-12-24 20:00:00 0.1
## 690 2024-12-24 21:00:00 0.1
## 691 2024-12-24 22:00:00 0.1
## 692 2024-12-24 23:00:00 0.1
## 693 2024-12-25 00:00:00 0.1
## 694 2024-12-25 01:00:00 0.1
## 695 2024-12-25 02:00:00 0.1
## 696 2024-12-25 03:00:00 0.1
## 697 2024-12-25 04:00:00 0.1
## 698 2024-12-25 05:00:00 0.1
## 699 2024-12-25 06:00:00 0.1
## 700 2024-12-25 07:00:00 0.1
## 701 2024-12-25 08:00:00 0.1
## 702 2024-12-25 09:00:00 0.1
## 703 2024-12-25 10:00:00 0.1
## 704 2024-12-25 11:00:00 0.1
## 705 2024-12-25 12:00:00 0.1
## 706 2024-12-25 13:00:00 0.1
## 707 2024-12-25 14:00:00 0.1
## 708 2024-12-25 15:00:00 0.1
## 709 2024-12-25 16:00:00 0.1
## 710 2024-12-25 17:00:00 0.1
## 711 2024-12-25 18:00:00 0.1
## 712 2024-12-25 19:00:00 0.1
## 713 2024-12-25 20:00:00 0.1
## 714 2024-12-25 21:00:00 0.1
## 715 2024-12-25 22:00:00 0.1
## 716 2024-12-25 23:00:00 0.1
## 717 2024-12-26 00:00:00 0.1
## 718 2024-12-26 01:00:00 0.1
## 719 2024-12-26 02:00:00 0.1
## 720 2024-12-26 03:00:00 0.1
## 721 2024-12-26 04:00:00 0.1
## 722 2024-12-26 05:00:00 0.1
## 723 2024-12-26 06:00:00 0.1
## 724 2024-12-26 07:00:00 0.1
## 725 2024-12-26 08:00:00 0.1
## 726 2024-12-26 09:00:00 0.1
## 727 2024-12-26 10:00:00 0.1
## 728 2024-12-26 11:00:00 0.1
## 729 2024-12-26 12:00:00 0.1
## 730 2024-12-26 13:00:00 0.1
## 731 2024-12-26 14:00:00 0.1
## 732 2024-12-26 15:00:00 0.1
## 733 2024-12-26 16:00:00 0.1
## 734 2024-12-26 17:00:00 0.1
## 735 2024-12-26 18:00:00 0.1
## 736 2024-12-26 19:00:00 0.1
## 737 2024-12-26 20:00:00 0.1
## 738 2024-12-26 21:00:00 0.1
## 739 2024-12-26 22:00:00 0.1
## 740 2024-12-26 23:00:00 0.1
## 741 2024-12-27 00:00:00 0.1
## 742 2024-12-27 01:00:00 0.1
## 743 2024-12-27 02:00:00 0.1
## 744 2024-12-27 03:00:00 0.1
## 745 2024-12-27 04:00:00 0.1
## 746 2024-12-27 05:00:00 0.1
## 747 2024-12-27 06:00:00 0.1
## 748 2024-12-27 07:00:00 0.1
## 749 2024-12-27 08:00:00 0.1
## 750 2024-12-27 09:00:00 0.1
## 751 2024-12-27 10:00:00 0.1
## 752 2024-12-27 11:00:00 0.1
## 753 2024-12-27 12:00:00 0.1
## 754 2024-12-27 13:00:00 0.1
## 755 2024-12-27 14:00:00 0.1
## 756 2024-12-27 15:00:00 0.1
## 757 2024-12-27 16:00:00 0.1
## 758 2024-12-27 17:00:00 0.1
## 759 2024-12-27 18:00:00 0.1
## 760 2024-12-27 19:00:00 0.1
## 761 2024-12-27 20:00:00 0.1
## 762 2024-12-27 21:00:00 0.1
## 763 2024-12-27 22:00:00 0.1
## 764 2024-12-27 23:00:00 0.1
## 765 2024-12-28 00:00:00 0.1
## 766 2024-12-28 01:00:00 0.1
## 767 2024-12-28 02:00:00 0.1
## 768 2024-12-28 03:00:00 0.1
## 769 2024-12-28 04:00:00 0.1
## 770 2024-12-28 05:00:00 0.1
## 771 2024-12-28 06:00:00 0.1
## 772 2024-12-28 07:00:00 0.1
## 773 2024-12-28 08:00:00 0.1
## 774 2024-12-28 09:00:00 0.1
## 775 2024-12-28 10:00:00 0.1
## 776 2024-12-28 11:00:00 0.1
## 777 2024-12-28 12:00:00 0.1
## 778 2024-12-28 13:00:00 0.1
## 779 2024-12-28 14:00:00 0.1
## 780 2024-12-28 15:00:00 0.1
## 781 2024-12-28 16:00:00 0.1
## 782 2024-12-28 17:00:00 0.1
## 783 2024-12-28 18:00:00 0.1
## 784 2024-12-28 19:00:00 0.1
## 785 2024-12-28 20:00:00 0.1
## 786 2024-12-28 21:00:00 0.1
## 787 2024-12-28 22:00:00 0.1
## 788 2024-12-28 23:00:00 0.1
## 789 2024-12-29 00:00:00 0.1
## 790 2024-12-29 01:00:00 0.1
## 791 2024-12-29 02:00:00 0.1
## 792 2024-12-29 03:00:00 0.1
## 793 2024-12-29 04:00:00 0.1
## 794 2024-12-29 05:00:00 0.1
## 795 2024-12-29 06:00:00 0.1
## 796 2024-12-29 07:00:00 0.1
## 797 2024-12-29 08:00:00 0.1
## 798 2024-12-29 09:00:00 0.1
## 799 2024-12-29 10:00:00 0.1
## 800 2024-12-29 11:00:00 0.1
## 801 2024-12-29 12:00:00 0.1
## 802 2024-12-29 13:00:00 0.1
## 803 2024-12-29 14:00:00 0.1
## 804 2024-12-29 15:00:00 0.1
## 805 2024-12-29 16:00:00 0.1
## 806 2024-12-29 17:00:00 0.1
## 807 2024-12-29 18:00:00 0.1
## 808 2024-12-29 19:00:00 0.1
## 809 2024-12-29 20:00:00 0.1
## 810 2024-12-29 21:00:00 0.1
## 811 2024-12-29 22:00:00 0.1
## 812 2024-12-29 23:00:00 0.1
## 813 2024-12-30 00:00:00 0.1
## 814 2024-12-30 01:00:00 0.1
## 815 2024-12-30 02:00:00 0.1
## 816 2024-12-30 03:00:00 0.1
## 817 2024-12-30 04:00:00 0.1
## 818 2024-12-30 05:00:00 0.1
## 819 2024-12-30 06:00:00 0.1
## 820 2024-12-30 07:00:00 0.1
## 821 2024-12-30 08:00:00 0.1
## 822 2024-12-30 09:00:00 0.1
## 823 2024-12-30 10:00:00 0.1
## 824 2024-12-30 11:00:00 0.1
## 825 2024-12-30 12:00:00 0.1
## 826 2024-12-30 13:00:00 0.1
## 827 2024-12-30 14:00:00 0.1
## 828 2024-12-30 15:00:00 0.1
## 829 2024-12-30 16:00:00 0.1
## 830 2024-12-30 17:00:00 0.1
## 831 2024-12-30 18:00:00 0.1
## 832 2024-12-30 19:00:00 0.1
## 833 2024-12-30 20:00:00 0.1
## 834 2024-12-30 21:00:00 0.1
## 835 2024-12-30 22:00:00 0.1
## 836 2024-12-30 23:00:00 0.1
## 837 2024-12-31 00:00:00 0.1
## 838 2024-12-31 01:00:00 0.1
## 839 2024-12-31 02:00:00 0.1
## 840 2024-12-31 03:00:00 0.1
## 841 2024-12-31 04:00:00 0.1
## 842 2024-12-31 05:00:00 0.1
## 843 2024-12-31 06:00:00 0.1
## 844 2024-12-31 07:00:00 0.1
## 845 2024-12-31 08:00:00 0.1
## 846 2024-12-31 09:00:00 0.1
## 847 2024-12-31 12:00:00 0.1
## 848 2024-12-31 13:00:00 0.1
## 849 2024-12-31 14:00:00 0.1
## 850 2024-12-31 15:00:00 0.1
## 851 2024-12-31 16:00:00 0.1
## 852 2024-12-31 17:00:00 0.1
## 853 2024-12-31 18:00:00 0.1
## 854 2024-12-31 19:00:00 0.1
## 855 2024-12-31 20:00:00 0.1
## 856 2024-12-31 21:00:00 0.1
## 857 2024-12-31 22:00:00 0.1
## 858 2024-12-31 23:00:00 0.1
Observamos que CO tiene el mismo valor siempre en Paiporta, probablemente debido a un error en el sensor, ya que es muy raro que el valor no varie en absoluto.
Ahora vamos a ver los valores máximos y minimos de cada estación.
df_co <- df %>%
filter(Origen %in% estaciones_co)
resumen_co <- df_co %>%
group_by(Origen) %>%
summarise(
min_CO = min(CO, na.rm = TRUE),
max_CO = max(CO, na.rm = TRUE)
) %>%
ungroup() %>%
arrange(Origen)
print(resumen_co)
## # A tibble: 11 × 3
## Origen min_CO max_CO
## <chr> <dbl> <dbl>
## 1 Catarroja UM 0.1 0.3
## 2 Massanassa_UM 0 0.3
## 3 Paiporta UM 0.1 0.1
## 4 Paterna - CEAM 0.1 1.7
## 5 Sedavi UM 0.1 0.3
## 6 Torrent-El Vedat 0.1 0.8
## 7 València - Av. França 0.1 1.5
## 8 València - Molí del Sol 0.1 1.4
## 9 València - Pista de Silla 0.1 1.6
## 10 València Port Moll Trans. Ponent 0.1 2.3
## 11 València Port llit antic Túria 0.1 1.4
ggplot(resumen_co, aes(x = Origen)) +
geom_linerange(aes(ymin = min_CO, ymax = max_CO), size = 1.5) +
geom_point(aes(y = min_CO), shape = 21, fill = "white", size = 3) +
geom_point(aes(y = max_CO), shape = 21, fill = "black", size = 3) +
coord_flip() +
labs(
title = "Rango de valores de CO por estación",
x = "Estación",
y = expression(CO~"(µg/m³)")
) +
theme_minimal(base_size = 12)
Algo que llama a atención es que el valor mínimo en todas las estaciones es 0.1 excepto en Massanassa que es 0. Además, el rango de valore de el puerto es casi el doble que todas los otros rangos, lo que indica que ha habido algun pico muy considerable.
df_co_gt2 <- df %>%
filter(
CO > 2
) %>%
select(FechaHora, CO)
print(df_co_gt2)
## FechaHora CO
## 1 2020-09-22 09:00:00 2.3
valores_origen <- df %>%
filter(
Fecha == as.Date("2020-09-22"),
Origen == "València Port Moll Trans. Ponent"
) %>%
mutate(Hora = factor(Hora, levels = unique(Hora))) %>%
select(Hora, `CO`) %>%
arrange(as.integer(as.character(Hora)))
ggplot(valores_origen, aes(x = Hora, y = `CO`, group = 1)) +
geom_line(color = "steelblue") +
geom_point(color = "steelblue", size = 2) +
scale_x_discrete() +
labs(
title = "CO por hora el 2020-09-22 (Origen: València Port Moll Trans. Ponent)",
x = "Hora",
y = expression(CO~"(µg/m"^3*")")
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Se observa un pico muy alto durante 1 hora que luego vuelve a sus valores normales, esto podría tratarse de un error en el sensor.
Ahora, vamos a estudiar si hay diferencias significativas en los valores de CO dependiendo de la estación de medición. Ya que no podemos usar un ANOVA, vamos a utilizar el test de Kruksal-Wallis para comprobar si exite al menos una diferencia entre varios origenes.
kruskal.test(log_CO ~ Origen, data = df)
##
## Kruskal-Wallis rank sum test
##
## data: log_CO by Origen
## Kruskal-Wallis chi-squared = 12209, df = 10, p-value < 2.2e-16
Dado que el p-values es inferior a 0.05, podemos rechazar la hipotesis nula y confirmar que hay evidencia de diferencias significativas entre distribuciones de CO según el Origen.
Ahora vamos a realizar el test de Dunn, para realizar las comparaciones por pares para saber que niveles de Origen difieren.
df %>%
dunn_test(log_CO ~ Origen, p.adjust.method = "bonferroni")
## # A tibble: 55 × 9
## .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif
## * <chr> <chr> <chr> <int> <int> <dbl> <dbl> <dbl> <chr>
## 1 log_CO Catarro… Massa… 894 1160 0.560 5.75e- 1 1 e+ 0 ns
## 2 log_CO Catarro… Paipo… 894 855 -0.197 8.44e- 1 1 e+ 0 ns
## 3 log_CO Catarro… Pater… 894 48069 16.3 1.52e- 59 8.34e- 58 ****
## 4 log_CO Catarro… Sedav… 894 744 2.22 2.65e- 2 1 e+ 0 ns
## 5 log_CO Catarro… Torre… 894 11363 5.24 1.62e- 7 8.89e- 6 ****
## 6 log_CO Catarro… Valèn… 894 47396 14.3 3.73e- 46 2.05e- 44 ****
## 7 log_CO Catarro… Valèn… 894 48780 13.3 2.38e- 40 1.31e- 38 ****
## 8 log_CO Catarro… Valèn… 894 49037 26.5 1.11e-154 6.09e-153 ****
## 9 log_CO Catarro… Valèn… 894 28249 24.9 6.12e-137 3.37e-135 ****
## 10 log_CO Catarro… Valèn… 894 36930 23.3 1.12e-119 6.15e-118 ****
## # ℹ 45 more rows
Las estaciones con **** indican que difieren significativamente, las demás no tienen diferencias significativas. Esas estaciones, sin diferencias significativas, son las siguientes:
Estas estaciones estan todas relativamente cercas unas de otras, y por ejemplo todas sufrieron mucho durante la DANA, lo que puede llevar a la conclusión de que se comportan similarmente y que no difieren entre ellas pero si entre las demás.
En conclusión, la zona si que influye en los valores medidos de CO.
Vamos a ver como se comporta el contaminante a lo largo del tiempo. Para ello, vamos a hacer un gráfico.
df_anual_co <- df %>%
mutate(Año = as.integer(as.character(Año))) %>%
group_by(Año) %>%
summarise(media_co = mean(CO, na.rm = TRUE)) %>%
arrange(Año)
ggplot(df_anual_co, aes(x = Año, y = media_co)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(
breaks = 2019:2024,
limits = c(2019, 2024)
) +
labs(
title = "Yearly evolution of the average CO concentration",
subtitle = "Average values from all stations in Valencia",
x = "Year",
y = "Average of CO (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
En el caso de CO, observamos como de 2019 a 2022 los valores tienden a disminuir de una forma constante, pero hay un pico en 2023. Segun información que hemos encontrado en internet,de 2016 a 2022, se redujo la cantidad de carriles en varias zonas de valencia, lo que pudo causar esa disminución que se ve en la gráfica. Y en 2023 esa ley se retiró, lo que puedo causar ese pico en 2023.
Vamos a ver como varían los valores en cada año.
df_anual_co_sd <- df %>%
mutate(Año = as.integer(as.character(Año))) %>%
group_by(Año) %>%
summarise(sd_co = sd(CO, na.rm = TRUE)) %>%
arrange(Año)
ggplot(df_anual_co_sd, aes(x = Año, y = sd_co)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_continuous(
breaks = 2019:2024,
limits = c(2019, 2024)
) +
labs(
title = "Yearly evolution of the standarad deviation CO concentration",
subtitle = "Standard deviation values from all stations in Valencia",
x = "Year",
y = "SD of CO (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Observamos que en procporciona los valores de cada año, la desviacion típica se matiene constante. Lo que sugiere que pese a que los valores bajan y aumentan dependiendo del año, su variación no cambia.
Con estas gráficas, podemos observar que si que existen diferencias significativas en los valores de CO.
df_mensual_co <- df %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
group_by(Mes) %>%
summarise(media_co = mean(CO, na.rm = TRUE)) %>%
arrange(Mes) %>%
mutate(mes_num = as.integer(Mes))
ggplot(df_mensual_co, aes(x = mes_num, y = media_co)) +
geom_area(fill = "#FFEB3B", alpha = 0.4) +
geom_line(color = "#FFC107", size = 1.2) +
geom_point(color = "#FFC107", size = 3) +
scale_x_continuous(
breaks = 1:12,
labels = meses_orden,
limits = c(1, 12)
) +
labs(
title = "Evolución anual de la concentración promedio de CO",
subtitle = "Promedios mensuales desde todas las estaciones en Valencia",
x = "Mes",
y = "Promedio de CO (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#FFC107"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Podemos observar que la media de CO es más alta en los meses de frío, lo que puede deberse a que existe una relación entre CO y la temperatura. Esto los comprobaremos más adelante.
Vamos a ver que sucede con las desviaciones típicas.
df_mensual_co_sd <- df %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
group_by(Mes) %>%
summarise(sd_co = sd(CO, na.rm = TRUE)) %>%
arrange(Mes) %>%
mutate(mes_num = as.integer(Mes))
ggplot(df_mensual_co_sd, aes(x = mes_num, y = sd_co)) +
geom_area(fill = "#FFEB3B", alpha = 0.4) +
geom_line(color = "#FFC107", size = 1.2) +
geom_point(color = "#FFC107", size = 3) +
scale_x_continuous(
breaks = 1:12,
labels = meses_orden,
limits = c(1, 12)
) +
labs(
title = "Evolución anual de la desviación típica de CO",
subtitle = "Desviaciones típicas mensuales desde todas las estaciones en Valencia",
x = "Mes",
y = "SD de CO (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#FFC107"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Observamos una gŕafica parecida a la de la media, por lo que la variación de los valores en cada mes es proporcionalmente igual en todos los mese.
Podemos confirmar que si que existe una relación entre los meses del año y los valores de CO. Probablemente debido a la temperatura, lo cual estudiaremos más adelante.
df_semanal_co <- df %>%
mutate(
Dia_Semana = factor(Dia_Semana, levels = dias_orden, ordered = TRUE)
) %>%
group_by(Dia_Semana) %>%
summarise(media_co = mean(CO, na.rm = TRUE)) %>%
arrange(Dia_Semana) %>%
mutate(dia_num = as.integer(Dia_Semana))
ggplot(df_semanal_co, aes(x = dia_num, y = media_co)) +
geom_area(fill = "#2196F3", alpha = 0.4) +
geom_line(color = "#1976D2", size = 1.2) +
geom_point(color = "#1976D2", size = 3) +
scale_x_continuous(
breaks = 1:7,
labels = dias_orden,
limits = c(1, 7)
) +
labs(
title = "Evolución semanal de la concentración promedio de CO",
subtitle = "Promedios semanales desde todas las estaciones en Valencia",
x = "Día de la semana",
y = "Promedio de CO (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#1976D2"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Podemos ver que la media de CO no varia mucho a lo largo de la semana, excepto el fin de semana que parece disminuir un poco. Esto puede deberse a que es un contaminante muy estatico, es decir que cuesta aumentar sus niveles.
df_semanal_co_sd <- df %>%
mutate(
Dia_Semana = factor(Dia_Semana, levels = dias_orden, ordered = TRUE)
) %>%
group_by(Dia_Semana) %>%
summarise(sd_co = sd(CO, na.rm = TRUE)) %>%
arrange(Dia_Semana) %>%
mutate(dia_num = as.integer(Dia_Semana))
ggplot(df_semanal_co_sd, aes(x = dia_num, y = sd_co)) +
geom_area(fill = "#2196F3", alpha = 0.4) +
geom_line(color = "#1976D2", size = 1.2) +
geom_point(color = "#1976D2", size = 3) +
scale_x_continuous(
breaks = 1:7,
labels = dias_orden,
limits = c(1, 7)
) +
labs(
title = "Evolución semanal de la desviación estandar de CO",
subtitle = "Desviaciones típicas semanales desde todas las estaciones en Valencia",
x = "Día de la semana",
y = "SD de CO (µg/m³)",
caption = "Fuente: datos de contaminación del aire"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#1976D2"),
axis.text = element_text(color = "gray20"),
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Las desviaciones típicas son, en proporción, constantes. En definitiva, no parece haber una diferencia significativa entre los valores de CO en cada dia de la semana. Pese a que el fin de semana, especialmente el domingo, si disminuyen un poco en comparación a el resto de la semana, pero una cantidad muy pequeña.
Esto podría deberse a muchas razones, como por ejemplo el tráfico, lo cual estudiaremos más adelante.
df_hourly_co <- df %>%
group_by(Hora) %>%
summarise(media_co = mean(CO, na.rm = TRUE)) %>%
arrange(Hora)
ggplot(df_hourly_co, aes(x = factor(Hora, levels = 0:23), y = media_co, group = 1)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_discrete(drop = FALSE) +
labs(
title = "Hourly evolution of the average CO concentration",
subtitle = "Average values from all stations in Valencia",
x = "Hour of the day",
y = "Average of CO (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
El contaminante CO, tiene un comportamiento similar al de PM10. Un aumento sobre las 6 de la mañana y otro aumento hacia la noche. Esto puede deberse a el tráfico, ya que a esas horas la gente suele entrar y salir de trabajar.
Ahora vamos a ver como son las desviaciones típicas para cada hora del dia.
df_hourly_co_sd <- df %>%
group_by(Hora) %>%
summarise(sd_co = sd(CO, na.rm = TRUE)) %>%
arrange(Hora)
ggplot(df_hourly_co_sd, aes(x = factor(Hora, levels = 0:23), y = sd_co, group = 1)) +
geom_area(fill = "#FF6E6E", alpha = 0.4) +
geom_line(color = "#B22222", size = 1.2) +
geom_point(color = "#B22222", size = 3) +
scale_x_discrete(drop = FALSE) +
labs(
title = "Hourly evolution of the standard deviation of CO ",
subtitle = "Standard deviations values from all stations in Valencia",
x = "Hour of the day",
y = "SD of CO (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold", color = "#B22222"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Observamos la misma distribución que en la media, es decir que proporcionalmennte, los valores varían igual independientemente de la hora del día.
Podemos concluir que si que existen diferencias significativas entre los valores de CO por horas, probablemente debido al tráfico rodado. Esto se estudiará más adelante.
Primero de todo, vamos a ver el porcentaje de faltantes.
sum(is.na(df$CO)) / nrow(df) * 100
## [1] 59.10606
Lo que llama más la atención es que esta variable tiene un porcentaje de nulos mucho superior a la de los otros contaminantes estudiados. Vamos a ver en que zonas hay más faltantes de esta variable.
df %>%
filter(is.na(CO)) %>%
count(Origen) %>%
arrange(desc(n)) %>%
ggplot(aes(x = reorder(Origen, -n), y = n)) +
geom_bar(stat = "identity", fill = "firebrick") +
labs(title = "Número de valores faltantes (NA) en CO por estación",
x = "Estación (Origen)",
y = "Número de NAs") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Podemos observar que hay 8 estaciones con una cantidad muy alta de faltantes, lo cual puede deberse a que no se mide CO en esas estaciones. Vamos a inspeccionar que estaciones la miden.
resumen_so2 <- df %>%
select(Origen, CO) %>%
pivot_longer(
cols = -Origen,
names_to = "Variable",
values_to = "Valor"
) %>%
filter(Variable == "CO", !is.na(Valor)) %>%
group_by(Variable) %>%
summarise(
num_estaciones = n_distinct(Origen),
lista_estaciones = paste(sort(unique(Origen)), collapse = ", ")
) %>%
ungroup()
print(resumen_so2)
## # A tibble: 1 × 3
## Variable num_estaciones lista_estaciones
## <chr> <int> <chr>
## 1 CO 11 Catarroja UM, Massanassa_UM, Paiporta UM, Paterna - C…
La variable CO no se mide en las siguientes estaciones:
Ahora vamos a ver el los faltantes en las estaciones que si miden CO.
df_plot <- df %>%
filter(Origen %in% estaciones_co) %>%
group_by(Origen) %>%
summarise(
n = sum(is.na(CO)),
total = n(),
pct = n / total * 100
) %>%
arrange(desc(n))
ggplot(df_plot, aes(x = reorder(Origen, -n), y = n)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct,1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Número y % de valores faltantes (NA) en CO por estación",
x = "Estación (Origen)",
y = "Número de NAs"
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1)
)
Al igual que en los otros contaminantes, la gran mayoría de datos faltantes estan presentes en Torrent. Ahora vamos a ver el porcentaje de faltantes que hay ahora.
resumen_total_co <- df %>%
filter(Origen %in% estaciones_co) %>%
summarise(
total_obs = n(),
faltantes = sum(is.na(CO)),
pct_faltantes = faltantes / total_obs * 100
)
print(resumen_total_co)
## total_obs faltantes pct_faltantes
## 1 294275 20798 7.067539
Observamos que tiene un 7% de faltantes, lo cual es más alto que PM2.5 y PM10. Para estudiar más a fondo la distribución de los faltantes, vamos a ver si los faltantes por año son iguales:
df_plot <- df %>%
filter(
Origen %in% estaciones_co,
is.na(CO)
) %>%
count(Año, name = "n_missing_CO") %>%
ungroup() %>%
mutate(
total_missing_CO = sum(n_missing_CO),
pct_total = n_missing_CO / total_missing_CO * 100
) %>%
arrange(Año)
ggplot(df_plot, aes(x = factor(Año), y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de CO por año",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese año",
x = "Año",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
Observamos que en 2023 hay una cantidad de faltantes mayor, y que en 2024 este valor disminuye mucho en comparación a el resto de años.
Ahora vamos a ver su distribucion por meses.
df_plot <- df %>%
filter(Origen %in% estaciones_co) %>%
mutate(Mes = factor(Mes, levels = meses_orden, ordered = TRUE)) %>%
filter(is.na(CO)) %>%
count(Mes, name = "n_missing") %>%
ungroup() %>%
mutate(
total_missing = sum(n_missing),
pct_total = n_missing / total_missing * 100
)
ggplot(df_plot, aes(x = Mes, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de CO por mes",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese mes",
x = "Mes",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5))
En el caso de los meses, la distribucion de los faltantes es parecida a los contaminantes PM10, PM2.5 y SO2. Con incremento a lo largo de los meses, excepto Febrero.
Ahora vamos a hacer lo mismo para dias de la semana, que en PM10, PM2.5 y SO2 estaban más o menos uniformemente distribuidos.
df_plot <- df %>%
filter(
Origen %in% estaciones_co,
is.na(CO)
) %>%
count(Dia_Semana, name = "n_missing") %>%
ungroup() %>%
mutate(
total_missing = sum(n_missing),
pct_total = n_missing / total_missing * 100
) %>%
mutate(Dia_Semana = factor(Dia_Semana, levels = dias_orden)) %>%
arrange(Dia_Semana)
ggplot(df_plot, aes(x = Dia_Semana, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
labs(
title = "Porcentaje de valores faltantes de CO por día de la semana",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en ese día",
x = "Día de la semana",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle= element_text(hjust = 0.5)
)
En este caso, encontramos una distribución relativamente uniforme, teniendo una concentración de faltantes similar en cada dia.
Por último veamos si por horas tienen una distribución similar, en PM10, PM2.5 y SO2 era uniforme.
df_plot <- df %>%
filter(
Origen %in% estaciones_co,
is.na(CO)
) %>%
count(Hora, name = "n_missing") %>%
ungroup() %>%
mutate(
total_missing = sum(n_missing),
pct_total = n_missing / total_missing * 100 #
) %>%
arrange(Hora)
ggplot(df_plot, aes(x = Hora, y = pct_total)) +
geom_col(fill = "firebrick") +
geom_text(aes(label = paste0(round(pct_total, 1), "%")),
vjust = -0.5, size = 3) +
scale_x_continuous(breaks = 0:23) +
labs(
title = "Porcentaje de valores faltantes de CO por hora del día",
subtitle = "Cada barra muestra qué % del total de NAs ocurre en esa hora",
x = "Hora",
y = "% de NAs respecto al total"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle= element_text(hjust = 0.5)
)
Como esperábamos, tienen una distribución muy similar. El hecho de que tengan muchas similitudes en las distribuciones de los faltantes puede indicar un patrón.
Para si hay un patrón vamos a ver si un faltante se repite en otros años. Esto lo vamos a hacer siguiendo estos pasos:
-filtrar estaciones que miden la variable - Seleccionar filas donde la variable es NA - Agrupar por combinación de Mes, Dia y Hora - Calcular el numero de años distintos con al menos ese NA y contruimos la lista de años - Solo nos quedamos con los que se repiten en más de un año
df_faltantes_co <- df %>%
filter(Origen %in% estaciones_co)
faltantes_recurrentes_co <- df_faltantes_co %>%
filter(is.na(CO)) %>%
group_by(Mes, Dia, Hora) %>%
summarise(
n_años = n_distinct(Año),
Años_con_faltante = paste(sort(unique(Año)), collapse = ", "),
.groups = "drop"
) %>%
filter(n_años > 1) %>%
select(Mes, Dia, Hora, Años_con_faltante)
print(faltantes_recurrentes_co)
## # A tibble: 6,324 × 4
## Mes Dia Hora Años_con_faltante
## <chr> <int> <int> <chr>
## 1 Abril 2 2 2021, 2023
## 2 Abril 2 3 2021, 2023
## 3 Abril 2 4 2021, 2023
## 4 Abril 2 5 2021, 2023
## 5 Abril 2 6 2021, 2023
## 6 Abril 2 7 2021, 2023
## 7 Abril 2 8 2019, 2021, 2023
## 8 Abril 2 9 2019, 2021, 2023
## 9 Abril 2 18 2021, 2023
## 10 Abril 2 19 2021, 2023
## # ℹ 6,314 more rows
Podemos observar que hay fechas y horas con faltantes que se repiten en más de un año, y algunas fechas son las mismas que en PM10, PM2.5 y SO2, por lo que parece haber un patrón.
Vamos a ver todo esto explicado, en una única gráfica:
df %>%
mutate(missing = is.na(CO)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c('TRUE' = "red", 'FALSE' = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de SO2",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
df %>%
filter(Origen %in% estaciones_co) %>%
mutate(missing = is.na(CO)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c('TRUE' = "red", 'FALSE' = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de SO2",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
En esta gráfica, podemos ver todo lo anterior, las estaciones en las que no se mide, que fechas tienen faltantes en varias estaciones, si esa misma fecha tiene faltantes en otros años,…
df_NOs <- read.csv("~/Documents/Drive/UPV/Segundo/2ºCuatri/Proyecto/base_para_obj1.csv", stringsAsFactors = FALSE)
df_NOs$FechaHora <- as.POSIXct(df_NOs$FechaHora, format = "%Y-%m-%d %H:%M:%S")
df_NOs$Origen <- as.factor(df_NOs$Origen)
Primero de todo vamos a ver la distribución de los datos de estos tres contaminantes individualmente.
hist((df_NOs$NOx), breaks = 100, main = "Distribución de NOx", xlab = "NOx", ylab = "Frecuencia")
hist((df_NOs$NO2), breaks = 100, main = "Distribución de NO2", xlab = "NO2", ylab = "Frecuencia")
hist((df_NOs$NO), breaks = 100, main = "Distribución de NO", xlab = "NO", ylab = "Frecuencia")
Las 3 variables tienen una distribución exponencial. Además, vemos que para el caso del \(NO\), la distribución está más sesgada a la derecha que para el \(NO_2\) y el \(NO_x\). Esto es debido a que el \(NO\) se oxida rápidamente a \(NO_2\), lo que provoca que el \(NO\) tenga una concentración más baja en comparación con el \(NO_2\).
Además, sabemos que el \(NO_x\) es el conjunto de todos los óxidos de nitrógeno, y se demuestra en la gráfica que el \(NO_x\) es la suma de los dos anteriores aproximadamente, ya que no se ha medido la presencia de otros óxidos de nitrógeno porque no son tan significantes en la contaminación del aire.
Vamos a ver cómo se comportan estos contaminantes a lo largo del tiempo.
ggplot(df_NOs, aes(x = FechaHora)) +
geom_line(aes(y = NOx, color = "NOx")) +
geom_line(aes(y = NO2, color = "NO2")) +
geom_line(aes(y = NO, color = "NO")) +
labs(title = "Contaminantes NOx, NO2 y NO a lo largo del tiempo",
x = "Fecha y Hora",
y = "Concentración (µg/m³)",
color = "Contaminante") +
theme_minimal()
## Warning: Removed 27888 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Removed 27888 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Removed 27888 rows containing missing values or values outside the scale range
## (`geom_line()`).
Podemos ver que los tres contaminantes tienen un comportamiento similar a lo largo del tiempo, con picos de concentración en los mismos momentos. Esto es debido a que los tres contaminantes están relacionados entre sí y provienen de las mismas fuentes de emisión, como el tráfico y la industria.
Además, los tres tienen la misma tendencia cada año, con picos en invierno y mínimos en verano.
En el año 2020, a partir de la época COVID, se registran valores que parecen ligeramente inferiores al resto de los años. Por ejemplo, en el inicio de 2021, pese a que se repite el patrón de todos los años, se hace con una menor concentración de estos contaminantes. Aproximadamente en noviembre de 2024 hay un pico repentino que no se repite en el resto de los años. Esto es debido a la DANA que hubo en esa época, que provocó una gran cantidad de lluvias y tormentas en la región, lo que puede haber afectado a la concentración de estos contaminantes en el aire.
Más adelante intentaremos darle una explicación a este patrón.
Vamos a calcular el promedio anual de cada contaminante para ver si hay alguna diferencia significativa entre años.
df_NOs_anual <- df_NOs %>%
group_by(year = year(FechaHora)) %>%
summarise(across(c("NOx", "NO2", "NO"), mean, na.rm = TRUE)) %>%
filter(!is.na(year)) %>%
ungroup()
df_long <- df_NOs_anual %>%
pivot_longer(
cols = c("NO", "NO2", "NOx"),
names_to = "Gas",
values_to = "Concentracion"
)
# 2) Gráfico con áreas superpuestas, líneas y puntos
ggplot(df_long, aes(x = year, y = Concentracion,
group = Gas, fill = Gas, color = Gas)) +
geom_area(alpha = 0.4, position = "identity") +
geom_line(size = 1.2) +
geom_point(size = 3) +
scale_x_continuous(breaks = 2019:2024, limits = c(2019, 2024)) +
scale_fill_manual(
values = c(
NO = "#FFD580",
NO2 = "#6EC6FF",
NOx = "#FF6E6E"
)
) +
scale_color_manual(
values = c(
NO = "#FF8C00",
NO2 = "#4682B4",
NOx = "#B22222"
)
) +
labs(
title = "Evolución anual de la concentración media de NO, NO2 y NOx",
subtitle = "Valores medios de todas las estaciones en Valencia",
x = "Año",
y = "Concentración media (µg/m³)",
fill = "Gas",
color = "Gas",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60"),
legend.position = "bottom"
)
Como era de esperar, los tres contaminantes tienen un comportamiento similar a lo largo de los años, con picos de concentración en los mismos momentos. Esto es debido a que los tres contaminantes están relacionados entre sí y provienen de las mismas fuentes de emisión, como el tráfico y la industria.
No obstante, el \(NO\) tiene menos variación que el \(NO_2\) y el \(NO_x\), y esto es debido a que este se oxida rápidamente a \(NO_2\).
Por otro lado, existe una tendencia en los tres contaminantes de disminuir su concentración a lo largo de los años. Esto puede ser debido a la implementación de políticas de reducción de emisiones y al aumento de la conciencia ambiental. Además, destaca la bajada importante en 2020 y 2021 por la época COVID, pero la tendencia a largo plazo es de bajada.
df_NOs_mensual <- df_NOs %>%
mutate(
month = month(FechaHora, label = TRUE, abbr = TRUE)
) %>%
group_by(month) %>%
summarise(
NO = mean(NO, na.rm = TRUE),
NO2 = mean(NO2, na.rm = TRUE),
NOx = mean(NOx, na.rm = TRUE),
.groups = "drop"
) %>%
pivot_longer(
cols = c("NO","NO2","NOx"),
names_to = "Gas",
values_to = "Concentracion"
) %>%
slice(1:(n() - 3))
# 2) Gráfico tipo área/línea/punto por mes
ggplot(df_NOs_mensual,
aes(x = month, y = Concentracion,
group = Gas, fill = Gas, color = Gas)) +
geom_area(alpha = 0.4, position = "identity") +
geom_line(size = 1.2) +
geom_point(size = 3) +
scale_fill_manual(
values = c(
NO = "#FFD580",
NO2 = "#6EC6FF",
NOx = "#FF6E6E"
)
) +
scale_color_manual(
values = c(
NO = "#FF8C00",
NO2 = "#4682B4",
NOx = "#B22222"
)
) +
labs(
title = "Evolución media mensual de NO, NO2 y NOx",
subtitle = "Media agregada sobre todos los años",
x = "Mes",
y = "Concentración media (µg/m³)",
fill = "Gas",
color = "Gas",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60"),
legend.position = "bottom"
)
En la evolución media mensual de los NOs se da el mismo comportamiento que en la evolución anual. Vemos además que en la época de invierno los valores son mucho más altos que en verano, mientras que en los meses de primavera y otoño los valores son intermedios entre los que se dan en invierno y verano.
Cuando hagamos el cruce con tráfico y contaminación le daremos una explicación a este comportamiento estacional.
df_NOs_diario <- df_NOs %>%
mutate(weekday = wday(FechaHora, label = TRUE, abbr = TRUE)) %>%
group_by(weekday) %>%
summarise(
NO = mean(NO, na.rm = TRUE),
NO2 = mean(NO2, na.rm = TRUE),
NOx = mean(NOx, na.rm = TRUE),
.groups = "drop"
) %>%
pivot_longer(
cols = c("NO","NO2","NOx"),
names_to = "Gas",
values_to = "Concentracion"
) %>%
slice(1:(n() - 3))
ggplot(df_NOs_diario,
aes(x = weekday, y = Concentracion,
group = Gas, fill = Gas, color = Gas)) +
geom_area(alpha = 0.4, position = "identity") +
geom_line(size = 1.2) +
geom_point(size = 3) +
scale_fill_manual(
values = c(
NO = "#FFD580",
NO2 = "#6EC6FF",
NOx = "#FF6E6E"
)
) +
scale_color_manual(
values = c(
NO = "#FF8C00",
NO2 = "#4682B4",
NOx = "#B22222"
)
) +
labs(
title = "Concentración media de NO, NO2 y NOx por día de la semana",
subtitle = "Agregado sobre todos los años",
x = "Día de la semana",
y = "Concentración media (µg/m³)",
fill = "Gas",
color = "Gas",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60"),
legend.position = "bottom"
)
Los 3 contaminantes tienen el mismo comportamiento entre ellos. Entre los días de la semana, vemos como entre semana los valores son mucho más altos que los fines de semana, donde decae bastante la concentración de los contaminantes.
df_NOs_horario <- df_NOs %>%
mutate(hour = hour(FechaHora)) %>%
group_by(hour) %>%
summarise(
NO = mean(NO, na.rm = TRUE),
NO2 = mean(NO2, na.rm = TRUE),
NOx = mean(NOx, na.rm = TRUE),
.groups = "drop"
) %>%
pivot_longer(
cols = c("NO","NO2","NOx"),
names_to = "Gas",
values_to = "Concentracion"
) %>%
slice(1:(n() - 3))
ggplot(df_NOs_horario,
aes(x = hour, y = Concentracion,
group = Gas, fill = Gas, color = Gas)) +
geom_area(alpha = 0.4, position = "identity") +
geom_line(size = 1.2) +
geom_point(size = 3) +
scale_x_continuous(breaks = 0:23, limits = c(0,23)) +
scale_fill_manual(
values = c(
NO = "#FFD580",
NO2 = "#6EC6FF",
NOx = "#FF6E6E"
)
) +
scale_color_manual(
values = c(
NO = "#FF8C00",
NO2 = "#4682B4",
NOx = "#B22222"
)
) +
labs(
title = "Concentración media de NO, NO2 y NOx por hora del día",
subtitle = "Media agregada sobre todos los años",
x = "Hora del día",
y = "Concentración media (µg/m³)",
fill = "Gas",
color = "Gas",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60"),
legend.position = "bottom"
)
Para las horas, vemos que los tres NOs siguen las mismas tendencias, pero en el caso de \(NO_x\) en la franja de 6 a 10 está mucho más pronunciada la concentración en comparación con los otros dos. Más adelante intentaremos buscarle una explicación a ese fenómeno.
Por otro lado, vemos que el día se puede dividir en cuatro grupos:
Al igual que antes, también le daremos una explicación al cruzar los contaminantes con otros factores.
Antes de pasar a estudiar el comportamiento por zonas, vamos a ver cómo se comportan los valores faltantes de estos contaminantes. Entendiendo esto, podríamos hacer una imputación correcta y precisa de los faltantes para potenciar posteriormente el análisis.
En primer lugar, vamos a ver el porcentaje de faltantes que tenemos para cada variable.
df_NOs %>% summarise(across(c("NOx", "NO2", "NO"), ~ sum(is.na(.)) / n() * 100))
## NOx NO2 NO
## 1 16.49503 16.49518 16.49503
El porcentaje de faltantes no es demasiado alto. Vamos a profundizar un poco más en el análisis de los valores faltantes viendo como se distribuyen por zonas.
missing_values_zona <- df_NOs %>%
group_by(Origen) %>%
summarise(across(c("NOx", "NO2", "NO"), ~ sum(is.na(.)) / n() * 100)) %>%
pivot_longer(cols = c("NOx", "NO2", "NO"), names_to = "Contaminante", values_to = "Porcentaje_Faltantes")
missing_values_zona <- missing_values_zona %>%
arrange(Porcentaje_Faltantes, Origen, Contaminante)
missing_values_zona
## # A tibble: 60 × 3
## Origen Contaminante Porcentaje_Faltantes
## <fct> <chr> <dbl>
## 1 València Olivereta NO 0
## 2 València Olivereta NO2 0
## 3 València Olivereta NOx 0
## 4 Catarroja UM NO 0.211
## 5 Catarroja UM NO2 0.211
## 6 Catarroja UM NOx 0.211
## 7 Paiporta UM NO 0.233
## 8 Paiporta UM NO2 0.233
## 9 Paiporta UM NOx 0.233
## 10 Paterna - CEAM NO 0.470
## # ℹ 50 more rows
Las estaciones “València - Nazaret Met-2” y “València-Conselleria Meteo” no tienen datos de los \(NO_x\). Por lo tanto, para el análisis de estos contaminantes no vamos a considerar estas dos estaciones.
df_NOs = df_NOs %>%
filter(Origen != "València - Nazaret Met-2" & Origen != "València-Conselleria Meteo")
Por otro lado, las estaciones tienen todas menos de un 3% de faltantes, a excepción de “Torrent-El Vedat” y “Benetusser UM”, que tienen un 41% y 60% respectivamente. Pese a ser valores ligeramente altos, vamos a incluirlas en el análisis para minimizar la pérdida de información y ver si podemos imputar los valores faltantes de forma correcta.
Vamos a ver cómo se distribuyen los valores faltantes de estos contaminantes a lo largo del tiempo, con el objetivo de detectar si hay algún patrón en la distribución de los valores faltantes.
df_NOs %>%
mutate(missing = is.na(NOx)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c(`TRUE` = "red", `FALSE` = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de NOx",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
## Warning: Removed 23961 rows containing missing values or values outside the scale range
## (`geom_tile()`).
df_NOs %>%
mutate(missing = is.na(NO)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c(`TRUE` = "red", `FALSE` = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de NOx",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
## Warning: Removed 23961 rows containing missing values or values outside the scale range
## (`geom_tile()`).
df_NOs %>%
mutate(missing = is.na(NO2)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c(`TRUE` = "red", `FALSE` = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de NOx",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
## Warning: Removed 23961 rows containing missing values or values outside the scale range
## (`geom_tile()`).
A la vista del gráfico, no se puede detectar ningún patrón en la distribución de los faltantes para cada variable individualmente, ya sea entre estaciones, entre años o en el conjunto de los dos.
Por otro lado, sí que vemos que los faltantes son los mismos para las tres variables óxidos de nitrógeno, probablemente debido a que se miden con los mismos sensores.
Vamos a intentar entender el comportamiento de estas variables en las distintas zonas donde se miden.
Para ellos vamos a realizar un ANOVA intentando ver si hay alguna diferencia significativa entre zonas.
En primer lugar vamos a ver la distribución de los valores de \(NO\) en función de la estación para evitar inconsistencias.
df_NOs %>%
filter(!is.na(NO)) %>%
ggplot(aes(x = NO)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de NO por estación",
x = "NO (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
No hay ninguna estación en la que la distribución sea distinta de las otras, aunque sí que cambia el grado de acentuación de la distribución de las estaciones. Por lo tanto, podemos realizar el ANOVA sin ningún problema.
anova_NO <- aov(NO ~ Origen, data = df_NOs)
summary(anova_NO)
## Df Sum Sq Mean Sq F value Pr(>F)
## Origen 17 7366669 433333 1683 <2e-16 ***
## Residuals 558419 143776217 257
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 16188 observations deleted due to missingness
Vemos que hay diferencias entre, al menos una de las estaciones. Ahora vamos a ver si se cumplen las hipótesis del ANOVA:
par(mfrow = c(1, 1))
plot(anova_NO)
dw <- dwtest(anova_NO)
print(dw)
##
## Durbin-Watson test
##
## data: anova_NO
## DW = 0.46209, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0
acf(anova_NO$residuals, main = "ACF de los residuos")
El anova no cumple con ninguna de las hipótesis iniciales del ANOVA. Vamos a intentar darle una explicación a cada una de ellas e intentar solucionarlo.
Normalidad de los residuos: Pese a que el anova es bastante robusto frente a esta hipótesis, se observa que tiene demasiada asimetría positiva como para validar esta hipótesis. Una posible solución es aplicar una transformación logarítmica a la variable \(NO\) para intentar normalizar los residuos.
Homocedasticidad: La homocedasticidad no se cumple, ya que los residuos tienen una varianza creciente a medida que aumenta el valor de la variable dependiente. Esto puede ser debido a la presencia de valores atípicos o a la falta de normalidad de los residuos. Una posible solución es aplicar una transformación logarítmica a la variable \(NO\) para intentar homogeneizar las varianzas.
Independencia de los residuos: La independencia de los residuos no se cumple, ya que se observa un patrón de subidas y bajadas en los residuos a lo largo del tiempo. Esto puede ser debido a que, como se ha visto al principio, la distribución del contaminante \(NO\) tiene un comportamiento estacional que se repite todos los años, lo que provoca que los residuos no sean independientes entre sí. Una posible solución es eliminar la temporalidad cogiendo una muestra de días aleatorios, y de todos ellos la misma hora.
Teniendo todo esto en cuenta, vamos a centrar el anova solo en 100 días aleatorios a las 12:00 del año 2023, que es el que recoge datos de más estaciones y evita episodios anómalos como el COVID-19 y la DANA. Además, vamos a aplicar el logaritmo de la variable \(NO\) para intentar normalizar los residuos y homogeneizar las varianzas.
# Seleccionar 100 filas aleatorias del año 2023 a las 12:00 el día lunes
set.seed(123) # Para reproducibilidad
df_NOs_2023 = df_NOs %>%
filter(FechaHora >= "2023-01-01" & FechaHora < "2024-01-01") %>%
filter(hour(FechaHora) == 12) %>%
filter(wday(FechaHora) == 2) %>%
sample_n(50)
df_NOs_2023$NO_log = log(df_NOs_2023$NO)
anova_NO_2023 <- aov(NO_log ~ Origen, data = df_NOs_2023)
summary(anova_NO_2023)
## Df Sum Sq Mean Sq F value Pr(>F)
## Origen 11 18.76 1.7054 4.908 0.000198 ***
## Residuals 32 11.12 0.3475
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 6 observations deleted due to missingness
par(mfrow = c(1, 1))
plot(anova_NO_2023)
dw <- dwtest(anova_NO_2023)
print(dw)
##
## Durbin-Watson test
##
## data: anova_NO_2023
## DW = 2.0519, p-value = 0.5163
## alternative hypothesis: true autocorrelation is greater than 0
leveneTest(anova_NO_2023)
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 11 1.737 0.1095
## 32
Tras realizar los cambios propuestos del anova 1, ahora sí que se cumplen las hipótesis del anova. Como el test F ha resultado significativo, rechazamos \(H_0\) y aceptamos que hay diferencias significativas entre al menos una de las estaciones. Para ver dónde están estas diferencias, vamos a realizar un test de comparación de medias. Concretamente, vamos a usar el test de Tukey, que es moderadamente conservador y tiene un buen control del error tipo I.
Tukey_NO <- TukeyHSD(anova_NO_2023, "Origen", conf.level = 0.95)
Tukey_NO
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = NO_log ~ Origen, data = df_NOs_2023)
##
## $Origen
## diff
## Quart de Poblet-Paterna - CEAM -0.23104906
## València - Av. França-Paterna - CEAM -0.27031007
## València - Bulevard Sud-Paterna - CEAM 0.82701932
## València - Centre-Paterna - CEAM 0.65220663
## València - Molí del Sol-Paterna - CEAM -0.73240819
## València - Pista de Silla-Paterna - CEAM 0.57550031
## València - Politècnic-Paterna - CEAM -1.19450631
## València - Vivers-Paterna - CEAM -0.29862658
## València Olivereta-Paterna - CEAM 1.47326685
## València Port llit antic Túria-Paterna - CEAM 0.30335982
## València Port Moll Trans. Ponent-Paterna - CEAM 0.13987821
## València - Av. França-Quart de Poblet -0.03926101
## València - Bulevard Sud-Quart de Poblet 1.05806838
## València - Centre-Quart de Poblet 0.88325569
## València - Molí del Sol-Quart de Poblet -0.50135913
## València - Pista de Silla-Quart de Poblet 0.80654937
## València - Politècnic-Quart de Poblet -0.96345725
## València - Vivers-Quart de Poblet -0.06757752
## València Olivereta-Quart de Poblet 1.70431591
## València Port llit antic Túria-Quart de Poblet 0.53440888
## València Port Moll Trans. Ponent-Quart de Poblet 0.37092727
## València - Bulevard Sud-València - Av. França 1.09732939
## València - Centre-València - Av. França 0.92251671
## València - Molí del Sol-València - Av. França -0.46209812
## València - Pista de Silla-València - Av. França 0.84581038
## València - Politècnic-València - Av. França -0.92419624
## València - Vivers-València - Av. França -0.02831651
## València Olivereta-València - Av. França 1.74357692
## València Port llit antic Túria-València - Av. França 0.57366990
## València Port Moll Trans. Ponent-València - Av. França 0.41018828
## València - Centre-València - Bulevard Sud -0.17481269
## València - Molí del Sol-València - Bulevard Sud -1.55942751
## València - Pista de Silla-València - Bulevard Sud -0.25151901
## València - Politècnic-València - Bulevard Sud -2.02152563
## València - Vivers-València - Bulevard Sud -1.12564590
## València Olivereta-València - Bulevard Sud 0.64624753
## València Port llit antic Túria-València - Bulevard Sud -0.52365950
## València Port Moll Trans. Ponent-València - Bulevard Sud -0.68714111
## València - Molí del Sol-València - Centre -1.38461483
## València - Pista de Silla-València - Centre -0.07670632
## València - Politècnic-València - Centre -1.84671295
## València - Vivers-València - Centre -0.95083321
## València Olivereta-València - Centre 0.82106021
## València Port llit antic Túria-València - Centre -0.34884681
## València Port Moll Trans. Ponent-València - Centre -0.51232843
## València - Pista de Silla-València - Molí del Sol 1.30790850
## València - Politècnic-València - Molí del Sol -0.46209812
## València - Vivers-València - Molí del Sol 0.43378161
## València Olivereta-València - Molí del Sol 2.20567504
## València Port llit antic Túria-València - Molí del Sol 1.03576802
## València Port Moll Trans. Ponent-València - Molí del Sol 0.87228640
## València - Politècnic-València - Pista de Silla -1.77000662
## València - Vivers-València - Pista de Silla -0.87412689
## València Olivereta-València - Pista de Silla 0.89776654
## València Port llit antic Túria-València - Pista de Silla -0.27214049
## València Port Moll Trans. Ponent-València - Pista de Silla -0.43562211
## València - Vivers-València - Politècnic 0.89587973
## València Olivereta-València - Politècnic 2.66777316
## València Port llit antic Túria-València - Politècnic 1.49786614
## València Port Moll Trans. Ponent-València - Politècnic 1.33438452
## València Olivereta-València - Vivers 1.77189343
## València Port llit antic Túria-València - Vivers 0.60198640
## València Port Moll Trans. Ponent-València - Vivers 0.43850479
## València Port llit antic Túria-València Olivereta -1.16990702
## València Port Moll Trans. Ponent-València Olivereta -1.33338864
## València Port Moll Trans. Ponent-València Port llit antic Túria -0.16348162
## lwr
## Quart de Poblet-Paterna - CEAM -1.9247197
## València - Av. França-Paterna - CEAM -1.7370719
## València - Bulevard Sud-Paterna - CEAM -1.0665621
## València - Centre-Paterna - CEAM -0.7792063
## València - Molí del Sol-Paterna - CEAM -2.4260789
## València - Pista de Silla-Paterna - CEAM -1.0087835
## València - Politècnic-Paterna - CEAM -3.0880877
## València - Vivers-Paterna - CEAM -1.8829104
## València Olivereta-Paterna - CEAM -0.1110170
## València Port llit antic Túria-Paterna - CEAM -1.5902216
## València Port Moll Trans. Ponent-Paterna - CEAM -1.4444056
## València - Av. França-Quart de Poblet -1.5060228
## València - Bulevard Sud-Quart de Poblet -0.8355130
## València - Centre-Quart de Poblet -0.5481573
## València - Molí del Sol-Quart de Poblet -2.1950298
## València - Pista de Silla-Quart de Poblet -0.7777345
## València - Politècnic-Quart de Poblet -2.8570386
## València - Vivers-Quart de Poblet -1.6518614
## València Olivereta-Quart de Poblet 0.1200321
## València Port llit antic Túria-Quart de Poblet -1.3591725
## València Port Moll Trans. Ponent-Quart de Poblet -1.2133566
## València - Bulevard Sud-València - Av. França -0.5963413
## València - Centre-València - Av. França -0.2315253
## València - Molí del Sol-València - Av. França -1.9288599
## València - Pista de Silla-València - Av. França -0.4931538
## València - Politècnic-València - Av. França -2.6178669
## València - Vivers-València - Av. França -1.3672807
## València Olivereta-València - Av. França 0.4046127
## València Port llit antic Túria-València - Av. França -1.1200008
## València Port Moll Trans. Ponent-València - Av. França -0.9287760
## València - Centre-València - Bulevard Sud -1.8379643
## València - Molí del Sol-València - Bulevard Sud -3.4530089
## València - Pista de Silla-València - Bulevard Sud -2.0479280
## València - Politècnic-València - Bulevard Sud -4.0958401
## València - Vivers-València - Bulevard Sud -2.9220549
## València Olivereta-València - Bulevard Sud -1.1501615
## València Port llit antic Túria-València - Bulevard Sud -2.5979740
## València Port Moll Trans. Ponent-València - Bulevard Sud -2.4835501
## València - Molí del Sol-València - Centre -2.8160278
## València - Pista de Silla-València - Centre -1.3768517
## València - Politècnic-València - Centre -3.5098645
## València - Vivers-València - Centre -2.2509786
## València Olivereta-València - Centre -0.4790852
## València Port llit antic Túria-València - Centre -2.0119984
## València Port Moll Trans. Ponent-València - Centre -1.8124738
## València - Pista de Silla-València - Molí del Sol -0.2763753
## València - Politècnic-València - Molí del Sol -2.3556795
## València - Vivers-València - Molí del Sol -1.1505022
## València Olivereta-València - Molí del Sol 0.6213912
## València Port llit antic Túria-València - Molí del Sol -0.8578134
## València Port Moll Trans. Ponent-València - Molí del Sol -0.7119974
## València - Politècnic-València - Pista de Silla -3.5664156
## València - Vivers-València - Pista de Silla -2.3408887
## València Olivereta-València - Pista de Silla -0.5689953
## València Port llit antic Túria-València - Pista de Silla -2.0685495
## València Port Moll Trans. Ponent-València - Pista de Silla -1.9023839
## València - Vivers-València - Politècnic -0.9005293
## València Olivereta-València - Politècnic 0.8713641
## València Port llit antic Túria-València - Politècnic -0.5764483
## València Port Moll Trans. Ponent-València - Politècnic -0.4620245
## València Olivereta-València - Vivers 0.3051316
## València Port llit antic Túria-València - Vivers -1.1944226
## València Port Moll Trans. Ponent-València - Vivers -1.0282570
## València Port llit antic Túria-València Olivereta -2.9663160
## València Port Moll Trans. Ponent-València Olivereta -2.8001505
## València Port Moll Trans. Ponent-València Port llit antic Túria -1.9598906
## upr
## Quart de Poblet-Paterna - CEAM 1.46262161
## València - Av. França-Paterna - CEAM 1.19645175
## València - Bulevard Sud-Paterna - CEAM 2.72060070
## València - Centre-Paterna - CEAM 2.08361961
## València - Molí del Sol-Paterna - CEAM 0.96126248
## València - Pista de Silla-Paterna - CEAM 2.15978416
## València - Politècnic-Paterna - CEAM 0.69907506
## València - Vivers-Paterna - CEAM 1.28565727
## València Olivereta-Paterna - CEAM 3.05755069
## València Port llit antic Túria-Paterna - CEAM 2.19694120
## València Port Moll Trans. Ponent-Paterna - CEAM 1.72416205
## València - Av. França-Quart de Poblet 1.42750081
## València - Bulevard Sud-Quart de Poblet 2.95164976
## València - Centre-Quart de Poblet 2.31466867
## València - Molí del Sol-Quart de Poblet 1.19231154
## València - Pista de Silla-Quart de Poblet 2.39083322
## València - Politècnic-Quart de Poblet 0.93012412
## València - Vivers-Quart de Poblet 1.51670633
## València Olivereta-Quart de Poblet 3.28859975
## València Port llit antic Túria-Quart de Poblet 2.42799026
## València Port Moll Trans. Ponent-Quart de Poblet 1.95521111
## València - Bulevard Sud-València - Av. França 2.79100006
## València - Centre-València - Av. França 2.07655874
## València - Molí del Sol-València - Av. França 1.00466371
## València - Pista de Silla-València - Av. França 2.18477462
## València - Politècnic-València - Av. França 0.76947443
## València - Vivers-València - Av. França 1.31064772
## València Olivereta-València - Av. França 3.08254115
## València Port llit antic Túria-València - Av. França 2.26734057
## València Port Moll Trans. Ponent-València - Av. França 1.74915251
## València - Centre-València - Bulevard Sud 1.48833889
## València - Molí del Sol-València - Bulevard Sud 0.33415386
## València - Pista de Silla-València - Bulevard Sud 1.54489001
## València - Politècnic-València - Bulevard Sud 0.05278883
## València - Vivers-València - Bulevard Sud 0.67076312
## València Olivereta-València - Bulevard Sud 2.44265655
## València Port llit antic Túria-València - Bulevard Sud 1.55065497
## València Port Moll Trans. Ponent-València - Bulevard Sud 1.10926791
## València - Molí del Sol-València - Centre 0.04679815
## València - Pista de Silla-València - Centre 1.22343908
## València - Politècnic-València - Centre -0.18356136
## València - Vivers-València - Centre 0.34931219
## València Olivereta-València - Centre 2.12120561
## València Port llit antic Túria-València - Centre 1.31430477
## València Port Moll Trans. Ponent-València - Centre 0.78781697
## València - Pista de Silla-València - Molí del Sol 2.89219235
## València - Politècnic-València - Molí del Sol 1.43148325
## València - Vivers-València - Molí del Sol 2.01806546
## València Olivereta-València - Molí del Sol 3.78995888
## València Port llit antic Túria-València - Molí del Sol 2.92934939
## València Port Moll Trans. Ponent-València - Molí del Sol 2.45657024
## València - Politècnic-València - Pista de Silla 0.02640240
## València - Vivers-València - Pista de Silla 0.59263494
## València Olivereta-València - Pista de Silla 2.36452836
## València Port llit antic Túria-València - Pista de Silla 1.52426854
## València Port Moll Trans. Ponent-València - Pista de Silla 1.03113972
## València - Vivers-València - Politècnic 2.69228876
## València Olivereta-València - Politècnic 4.46418218
## València Port llit antic Túria-València - Politècnic 3.57218060
## València Port Moll Trans. Ponent-València - Politècnic 3.13079354
## València Olivereta-València - Vivers 3.23865525
## València Port llit antic Túria-València - Vivers 2.39839543
## València Port Moll Trans. Ponent-València - Vivers 1.90526661
## València Port llit antic Túria-València Olivereta 0.62650200
## València Port Moll Trans. Ponent-València Olivereta 0.13337318
## València Port Moll Trans. Ponent-València Port llit antic Túria 1.63292741
## p adj
## Quart de Poblet-Paterna - CEAM 0.9999971
## València - Av. França-Paterna - CEAM 0.9999387
## València - Bulevard Sud-Paterna - CEAM 0.9185476
## València - Centre-Paterna - CEAM 0.8953724
## València - Molí del Sol-Paterna - CEAM 0.9233300
## València - Pista de Silla-Paterna - CEAM 0.9762319
## València - Politècnic-Paterna - CEAM 0.5482868
## València - Vivers-Paterna - CEAM 0.9999235
## València Olivereta-Paterna - CEAM 0.0875584
## València Port llit antic Túria-Paterna - CEAM 0.9999849
## València Port Moll Trans. Ponent-Paterna - CEAM 1.0000000
## València - Av. França-Quart de Poblet 1.0000000
## València - Bulevard Sud-Quart de Poblet 0.7110293
## València - Centre-Quart de Poblet 0.5798509
## València - Molí del Sol-Quart de Poblet 0.9952092
## València - Pista de Silla-Quart de Poblet 0.8107507
## València - Politècnic-Quart de Poblet 0.8112846
## València - Vivers-Quart de Poblet 1.0000000
## València Olivereta-Quart de Poblet 0.0262809
## València Port llit antic Túria-Quart de Poblet 0.9967982
## València Port Moll Trans. Ponent-Quart de Poblet 0.9993937
## València - Bulevard Sud-València - Av. França 0.5094361
## València - Centre-València - Av. França 0.2218463
## València - Molí del Sol-València - Av. França 0.9920202
## València - Pista de Silla-València - Av. França 0.5462938
## València - Politècnic-València - Av. França 0.7388398
## València - Vivers-València - Av. França 1.0000000
## València Olivereta-València - Av. França 0.0032716
## València Port llit antic Túria-València - Av. França 0.9859037
## València Port Moll Trans. Ponent-València - Av. França 0.9936410
## València - Centre-València - Bulevard Sud 0.9999998
## València - Molí del Sol-València - Bulevard Sud 0.1892257
## València - Pista de Silla-València - Bulevard Sud 0.9999962
## València - Politècnic-València - Bulevard Sud 0.0615419
## València - Vivers-València - Bulevard Sud 0.5579253
## València Olivereta-València - Bulevard Sud 0.9778581
## València Port llit antic Túria-València - Bulevard Sud 0.9987955
## València Port Moll Trans. Ponent-València - Bulevard Sud 0.9656842
## València - Molí del Sol-València - Centre 0.0652350
## València - Pista de Silla-València - Centre 1.0000000
## València - Politècnic-València - Centre 0.0193798
## València - Vivers-València - Centre 0.3346620
## València Olivereta-València - Centre 0.5466960
## València Port llit antic Túria-València - Centre 0.9997844
## València Port Moll Trans. Ponent-València - Centre 0.9579195
## València - Pista de Silla-València - Molí del Sol 0.1866734
## València - Politècnic-València - Molí del Sol 0.9991135
## València - Vivers-València - Molí del Sol 0.9975330
## València Olivereta-València - Molí del Sol 0.0013683
## València Port llit antic Túria-València - Molí del Sol 0.7360935
## València Port Moll Trans. Ponent-València - Molí del Sol 0.7285137
## València - Politècnic-València - Pista de Silla 0.0564034
## València - Vivers-València - Pista de Silla 0.6281607
## València Olivereta-València - Pista de Silla 0.5912767
## València Port llit antic Túria-València - Pista de Silla 0.9999915
## València Port Moll Trans. Ponent-València - Pista de Silla 0.9950760
## València - Vivers-València - Politècnic 0.8292742
## València Olivereta-València - Politècnic 0.0005491
## València Port llit antic Túria-València - Politècnic 0.3522547
## València Port Moll Trans. Ponent-València - Politècnic 0.3135076
## València Olivereta-València - Vivers 0.0079641
## València Port llit antic Túria-València - Vivers 0.9870186
## València Port Moll Trans. Ponent-València - Vivers 0.9947987
## València Port llit antic Túria-València Olivereta 0.5018912
## València Port Moll Trans. Ponent-València Olivereta 0.1026771
## València Port Moll Trans. Ponent-València Port llit antic Túria 1.0000000
Las estaciones entre las que hay una diferencia significativa de la concentración de \(NO\) son las siguientes:
df_NOs %>%
filter(!is.na(NO2)) %>%
ggplot(aes(x = NO2)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de NO2 por estación",
x = "NO2 (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
La distribución por estación de \(NO_2\) es muy parecida a la de \(NO\). Sin embargo, la estación “Benetusser UM” tiene una distribución más normal que las demás. Vamos a tener esto en cuenta por si saliera alguna anomalía en el anova.
Como hemos visto en la distribución y comportamiento a lo largo del tiempo, los tres óxidos de nitrógeno tienen un comportamiento muy parecido entre ellos. Por lo tanto, vamos a realizar el anova directamente con el logaritmo de \(NO_2\) y eliminando la dimensión del tiempo.
df_NOs_2023$NO2_log = log(df_NOs_2023$NO2)
anova_NO2_2023 <- aov(NO2_log ~ Origen, data = df_NOs_2023)
summary(anova_NO2_2023)
## Df Sum Sq Mean Sq F value Pr(>F)
## Origen 11 27.52 2.5020 5.417 8.35e-05 ***
## Residuals 32 14.78 0.4619
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 6 observations deleted due to missingness
par(mfrow = c(1, 1))
plot(anova_NO2_2023)
dw <- dwtest(anova_NO2_2023)
print(dw)
##
## Durbin-Watson test
##
## data: anova_NO2_2023
## DW = 1.5408, p-value = 0.05097
## alternative hypothesis: true autocorrelation is greater than 0
leveneTest(anova_NO2_2023)
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 11 0.747 0.6869
## 32
El anova cumple con las hipótesis iniciales. Como el test F ha resultado significativo, rechazamos \(H_0\) y aceptamos que hay diferencias significativas entre al menos una de las estaciones. Para ver dónde están estas diferencias, vamos a realizar un test de comparación de medias. Concretamente, vamos a usar el test de Tukey, que es moderadamente conservador y tiene un buen control del error tipo I.
Tukey_NO2 <- TukeyHSD(anova_NO2_2023, "Origen", conf.level = 0.95)
Tukey_NO2
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = NO2_log ~ Origen, data = df_NOs_2023)
##
## $Origen
## diff
## Quart de Poblet-Paterna - CEAM -0.13515504
## València - Av. França-Paterna - CEAM 0.62294494
## València - Bulevard Sud-Paterna - CEAM 0.78035520
## València - Centre-Paterna - CEAM 1.54672782
## València - Molí del Sol-Paterna - CEAM 0.65388617
## València - Pista de Silla-Paterna - CEAM 0.76387740
## València - Politècnic-Paterna - CEAM -0.17441605
## València - Vivers-Paterna - CEAM -0.49154388
## València Olivereta-Paterna - CEAM 1.81991819
## València Port llit antic Túria-Paterna - CEAM 0.02831651
## València Port Moll Trans. Ponent-Paterna - CEAM -0.62629300
## València - Av. França-Quart de Poblet 0.75809997
## València - Bulevard Sud-Quart de Poblet 0.91551024
## València - Centre-Quart de Poblet 1.68188285
## València - Molí del Sol-Quart de Poblet 0.78904120
## València - Pista de Silla-Quart de Poblet 0.89903244
## València - Politècnic-Quart de Poblet -0.03926101
## València - Vivers-Quart de Poblet -0.35638884
## València Olivereta-Quart de Poblet 1.95507322
## València Port llit antic Túria-Quart de Poblet 0.16347154
## València Port Moll Trans. Ponent-Quart de Poblet -0.49113797
## València - Bulevard Sud-València - Av. França 0.15741027
## València - Centre-València - Av. França 0.92378288
## València - Molí del Sol-València - Av. França 0.03094123
## València - Pista de Silla-València - Av. França 0.14093247
## València - Politècnic-València - Av. França -0.79736098
## València - Vivers-València - Av. França -1.11448882
## València Olivereta-València - Av. França 1.19697325
## València Port llit antic Túria-València - Av. França -0.59462843
## València Port Moll Trans. Ponent-València - Av. França -1.24923794
## València - Centre-València - Bulevard Sud 0.76637261
## València - Molí del Sol-València - Bulevard Sud -0.12646904
## València - Pista de Silla-València - Bulevard Sud -0.01647780
## València - Politècnic-València - Bulevard Sud -0.95477125
## València - Vivers-València - Bulevard Sud -1.27189908
## València Olivereta-València - Bulevard Sud 1.03956298
## València Port llit antic Túria-València - Bulevard Sud -0.75203870
## València Port Moll Trans. Ponent-València - Bulevard Sud -1.40664821
## València - Molí del Sol-València - Centre -0.89284165
## València - Pista de Silla-València - Centre -0.78285042
## València - Politècnic-València - Centre -1.72114387
## València - Vivers-València - Centre -2.03827170
## València Olivereta-València - Centre 0.27319037
## València Port llit antic Túria-València - Centre -1.51841131
## València Port Moll Trans. Ponent-València - Centre -2.17302082
## València - Pista de Silla-València - Molí del Sol 0.10999123
## València - Politècnic-València - Molí del Sol -0.82830222
## València - Vivers-València - Molí del Sol -1.14543005
## València Olivereta-València - Molí del Sol 1.16603202
## València Port llit antic Túria-València - Molí del Sol -0.62556966
## València Port Moll Trans. Ponent-València - Molí del Sol -1.28017917
## València - Politècnic-València - Pista de Silla -0.93829345
## València - Vivers-València - Pista de Silla -1.25542128
## València Olivereta-València - Pista de Silla 1.05604079
## València Port llit antic Túria-València - Pista de Silla -0.73556090
## València Port Moll Trans. Ponent-València - Pista de Silla -1.39017041
## València - Vivers-València - Politècnic -0.31712783
## València Olivereta-València - Politècnic 1.99433424
## València Port llit antic Túria-València - Politècnic 0.20273255
## València Port Moll Trans. Ponent-València - Politècnic -0.45187696
## València Olivereta-València - Vivers 2.31146207
## València Port llit antic Túria-València - Vivers 0.51986039
## València Port Moll Trans. Ponent-València - Vivers -0.13474913
## València Port llit antic Túria-València Olivereta -1.79160168
## València Port Moll Trans. Ponent-València Olivereta -2.44621119
## València Port Moll Trans. Ponent-València Port llit antic Túria -0.65460951
## lwr
## Quart de Poblet-Paterna - CEAM -2.087764266
## València - Av. França-Paterna - CEAM -1.068064260
## València - Bulevard Sud-Paterna - CEAM -1.402728281
## València - Centre-Paterna - CEAM -0.103528179
## València - Molí del Sol-Paterna - CEAM -1.298723061
## València - Pista de Silla-Paterna - CEAM -1.062621283
## València - Politècnic-Paterna - CEAM -2.357499533
## València - Vivers-Paterna - CEAM -2.318042566
## València Olivereta-Paterna - CEAM -0.006580498
## València Port llit antic Túria-Paterna - CEAM -2.154766979
## València Port Moll Trans. Ponent-Paterna - CEAM -2.452791691
## València - Av. França-Quart de Poblet -0.932909224
## València - Bulevard Sud-Quart de Poblet -1.267573245
## València - Centre-Quart de Poblet 0.031626857
## València - Molí del Sol-Quart de Poblet -1.163568025
## València - Pista de Silla-Quart de Poblet -0.927466247
## València - Politècnic-Quart de Poblet -2.222344497
## València - Vivers-Quart de Poblet -2.182887530
## València Olivereta-Quart de Poblet 0.128574538
## València Port llit antic Túria-Quart de Poblet -2.019611943
## València Port Moll Trans. Ponent-Quart de Poblet -2.317636655
## València - Bulevard Sud-València - Av. França -1.795198961
## València - Centre-València - Av. França -0.406696038
## València - Molí del Sol-València - Av. França -1.660067964
## València - Pista de Silla-València - Av. França -1.402740669
## València - Politècnic-València - Av. França -2.749970214
## València - Vivers-València - Av. França -2.658161952
## València Olivereta-València - Av. França -0.346699884
## València Port llit antic Túria-València - Av. França -2.547237660
## València Port Moll Trans. Ponent-València - Av. França -2.792911077
## València - Centre-València - Bulevard Sud -1.151051585
## València - Molí del Sol-València - Bulevard Sud -2.309552521
## València - Pista de Silla-València - Bulevard Sud -2.087532642
## València - Politècnic-València - Bulevard Sud -3.346219392
## València - Vivers-València - Bulevard Sud -3.342953925
## València Olivereta-València - Bulevard Sud -1.031491857
## València Port llit antic Túria-València - Bulevard Sud -3.143486838
## València Port Moll Trans. Ponent-València - Bulevard Sud -3.477703050
## València - Molí del Sol-València - Centre -2.543097648
## València - Pista de Silla-València - Centre -2.281769863
## València - Politècnic-València - Centre -3.638568066
## València - Vivers-València - Centre -3.537191145
## València Olivereta-València - Centre -1.225729077
## València Port llit antic Túria-València - Centre -3.435835512
## València Port Moll Trans. Ponent-València - Centre -3.671940270
## València - Pista de Silla-València - Molí del Sol -1.716507452
## València - Politècnic-València - Molí del Sol -3.011385702
## València - Vivers-València - Molí del Sol -2.971928735
## València Olivereta-València - Molí del Sol -0.660466667
## València Port llit antic Túria-València - Molí del Sol -2.808653148
## València Port Moll Trans. Ponent-València - Molí del Sol -3.106677860
## València - Politècnic-València - Pista de Silla -3.009348292
## València - Vivers-València - Pista de Silla -2.946430479
## València Olivereta-València - Pista de Silla -0.634968411
## València Port llit antic Túria-València - Pista de Silla -2.806615738
## València Port Moll Trans. Ponent-València - Pista de Silla -3.081179604
## València - Vivers-València - Politècnic -2.388182672
## València Olivereta-València - Politècnic -0.076720604
## València Port llit antic Túria-València - Politècnic -2.188715586
## València Port Moll Trans. Ponent-València - Politècnic -2.522931797
## València Olivereta-València - Vivers 0.620452872
## València Port llit antic Túria-València - Vivers -1.551194455
## València Port Moll Trans. Ponent-València - Vivers -1.825758322
## València Port llit antic Túria-València Olivereta -3.862656523
## València Port Moll Trans. Ponent-València Olivereta -4.137220390
## València Port Moll Trans. Ponent-València Port llit antic Túria -2.725664351
## upr
## Quart de Poblet-Paterna - CEAM 1.8174542
## València - Av. França-Paterna - CEAM 2.3139541
## València - Bulevard Sud-Paterna - CEAM 2.9634387
## València - Centre-Paterna - CEAM 3.1969838
## València - Molí del Sol-Paterna - CEAM 2.6064954
## València - Pista de Silla-Paterna - CEAM 2.5903761
## València - Politècnic-Paterna - CEAM 2.0086674
## València - Vivers-Paterna - CEAM 1.3349548
## València Olivereta-Paterna - CEAM 3.6464169
## València Port llit antic Túria-Paterna - CEAM 2.2114000
## València Port Moll Trans. Ponent-Paterna - CEAM 1.2002057
## València - Av. França-Quart de Poblet 2.4491092
## València - Bulevard Sud-Quart de Poblet 3.0985937
## València - Centre-Quart de Poblet 3.3321389
## València - Molí del Sol-Quart de Poblet 2.7416504
## València - Pista de Silla-Quart de Poblet 2.7255311
## València - Politècnic-Quart de Poblet 2.1438225
## València - Vivers-Quart de Poblet 1.4701098
## València Olivereta-Quart de Poblet 3.7815719
## València Port llit antic Túria-Quart de Poblet 2.3465550
## València Port Moll Trans. Ponent-Quart de Poblet 1.3353607
## València - Bulevard Sud-València - Av. França 2.1100195
## València - Centre-València - Av. França 2.2542618
## València - Molí del Sol-València - Av. França 1.7219504
## València - Pista de Silla-València - Av. França 1.6846056
## València - Politècnic-València - Av. França 1.1552482
## València - Vivers-València - Av. França 0.4291843
## València Olivereta-València - Av. França 2.7406464
## València Port llit antic Túria-València - Av. França 1.3579808
## València Port Moll Trans. Ponent-València - Av. França 0.2944352
## València - Centre-València - Bulevard Sud 2.6837968
## València - Molí del Sol-València - Bulevard Sud 2.0566144
## València - Pista de Silla-València - Bulevard Sud 2.0545770
## València - Politècnic-València - Bulevard Sud 1.4366769
## València - Vivers-València - Bulevard Sud 0.7991558
## València Olivereta-València - Bulevard Sud 3.1106178
## València Port llit antic Túria-València - Bulevard Sud 1.6394094
## València Port Moll Trans. Ponent-València - Bulevard Sud 0.6644066
## València - Molí del Sol-València - Centre 0.7574143
## València - Pista de Silla-València - Centre 0.7160690
## València - Politècnic-València - Centre 0.1962803
## València - Vivers-València - Centre -0.5393523
## València Olivereta-València - Centre 1.7721098
## València Port llit antic Túria-València - Centre 0.3990129
## València Port Moll Trans. Ponent-València - Centre -0.6741014
## València - Pista de Silla-València - Molí del Sol 1.9364899
## València - Politècnic-València - Molí del Sol 1.3547813
## València - Vivers-València - Molí del Sol 0.6810686
## València Olivereta-València - Molí del Sol 2.9925307
## València Port llit antic Túria-València - Molí del Sol 1.5575138
## València Port Moll Trans. Ponent-València - Molí del Sol 0.5463195
## València - Politècnic-València - Pista de Silla 1.1327614
## València - Vivers-València - Pista de Silla 0.4355879
## València Olivereta-València - Pista de Silla 2.7470500
## València Port llit antic Túria-València - Pista de Silla 1.3354939
## València Port Moll Trans. Ponent-València - Pista de Silla 0.3008388
## València - Vivers-València - Politècnic 1.7539270
## València Olivereta-València - Politècnic 4.0653891
## València Port llit antic Túria-València - Politècnic 2.5941807
## València Port Moll Trans. Ponent-València - Politècnic 1.6191779
## València Olivereta-València - Vivers 4.0024713
## València Port llit antic Túria-València - Vivers 2.5909152
## València Port Moll Trans. Ponent-València - Vivers 1.5562601
## València Port llit antic Túria-València Olivereta 0.2794532
## València Port Moll Trans. Ponent-València Olivereta -0.7552020
## València Port Moll Trans. Ponent-València Port llit antic Túria 1.4164453
## p adj
## Quart de Poblet-Paterna - CEAM 1.0000000
## València - Av. França-Paterna - CEAM 0.9736969
## València - Bulevard Sud-Paterna - CEAM 0.9788718
## València - Centre-Paterna - CEAM 0.0827144
## València - Molí del Sol-Paterna - CEAM 0.9870865
## València - Pista de Silla-Paterna - CEAM 0.9377927
## València - Politècnic-Paterna - CEAM 1.0000000
## València - Vivers-Paterna - CEAM 0.9978772
## València Olivereta-Paterna - CEAM 0.0515061
## València Port llit antic Túria-Paterna - CEAM 1.0000000
## València Port Moll Trans. Ponent-Paterna - CEAM 0.9845211
## València - Av. França-Quart de Poblet 0.9047903
## València - Bulevard Sud-Quart de Poblet 0.9367007
## València - Centre-Quart de Poblet 0.0426357
## València - Molí del Sol-Quart de Poblet 0.9502151
## València - Pista de Silla-Quart de Poblet 0.8403637
## València - Politècnic-Quart de Poblet 1.0000000
## València - Vivers-Quart de Poblet 0.9998927
## València Olivereta-Quart de Poblet 0.0275375
## València Port llit antic Túria-Quart de Poblet 1.0000000
## València Port Moll Trans. Ponent-Quart de Poblet 0.9978924
## València - Bulevard Sud-València - Av. França 1.0000000
## València - Centre-València - Av. França 0.4081515
## València - Molí del Sol-València - Av. França 1.0000000
## València - Pista de Silla-València - Av. França 1.0000000
## València - Politècnic-València - Av. França 0.9466665
## València - Vivers-València - Av. França 0.3525058
## València Olivereta-València - Av. França 0.2580778
## València Port llit antic Túria-València - Av. França 0.9939435
## València Port Moll Trans. Ponent-València - Av. França 0.2080304
## València - Centre-València - Bulevard Sud 0.9537090
## València - Molí del Sol-València - Bulevard Sud 1.0000000
## València - Pista de Silla-València - Bulevard Sud 1.0000000
## València - Politècnic-València - Bulevard Sud 0.9540516
## València - Vivers-València - Bulevard Sud 0.5865551
## València Olivereta-València - Bulevard Sud 0.8235922
## València Port llit antic Túria-València - Bulevard Sud 0.9921369
## València Port Moll Trans. Ponent-València - Bulevard Sud 0.4402408
## València - Molí del Sol-València - Centre 0.7484764
## València - Pista de Silla-València - Centre 0.7859598
## València - Politècnic-València - Centre 0.1118699
## València - Vivers-València - Centre 0.0018759
## València Olivereta-València - Centre 0.9999450
## València Port llit antic Túria-València - Centre 0.2327319
## València Port Moll Trans. Ponent-València - Centre 0.0007783
## València - Pista de Silla-València - Molí del Sol 1.0000000
## València - Politècnic-València - Molí del Sol 0.9675724
## València - Vivers-València - Molí del Sol 0.5567590
## València Olivereta-València - Molí del Sol 0.5309878
## València Port llit antic Túria-València - Molí del Sol 0.9963548
## València Port Moll Trans. Ponent-València - Molí del Sol 0.3945597
## València - Politècnic-València - Pista de Silla 0.8987606
## València - Vivers-València - Pista de Silla 0.3142256
## València Olivereta-València - Pista de Silla 0.5627520
## València Port llit antic Túria-València - Pista de Silla 0.9798518
## València Port Moll Trans. Ponent-València - Pista de Silla 0.1910606
## València - Vivers-València - Politècnic 0.9999905
## València Olivereta-València - Politècnic 0.0675458
## València Port llit antic Túria-València - Politècnic 1.0000000
## València Port Moll Trans. Ponent-València - Politècnic 0.9996864
## València Olivereta-València - Vivers 0.0017511
## València Port llit antic Túria-València - Vivers 0.9988558
## València Port Moll Trans. Ponent-València - Vivers 1.0000000
## València Port llit antic Túria-València Olivereta 0.1418783
## València Port Moll Trans. Ponent-València Olivereta 0.0008026
## València Port Moll Trans. Ponent-València Port llit antic Túria 0.9918084
Las estaciones entre las que hay una diferencia significativa de la concentración de \(NO_2\) son las siguientes:
df_NOs %>%
filter(!is.na(NOx)) %>%
ggplot(aes(x = NOx)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de NOx por estación",
x = "NOx (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
Igual que con la variable \(NO\), las distribuciones entre estaciones para la variable \(NO_x\) son muy parecidas. Sin embargo, en este caso la estación “Benetusser UM” tambień tiene una distribución ligeramente distinta a las demás, acumulando mayor presencia de datos extremos en la cola derecha. Vamos a realizar el anova y ver si hay alguna anomalía con esta estación.
Igual que en el caso del \(NO_2\), vamos a realizar el anova directamente con el logaritmo de \(NO_x\) y eliminando la dimensión del tiempo.
df_NOs_2023$NOx_log = log(df_NOs_2023$NOx)
anova_NOx_2023 <- aov(NOx_log ~ Origen, data = df_NOs_2023)
summary(anova_NOx_2023)
## Df Sum Sq Mean Sq F value Pr(>F)
## Origen 11 20.23 1.8391 5.697 5.28e-05 ***
## Residuals 32 10.33 0.3228
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 6 observations deleted due to missingness
par(mfrow = c(1, 1))
plot(anova_NOx_2023)
dw <- dwtest(anova_NOx_2023)
print(dw)
##
## Durbin-Watson test
##
## data: anova_NOx_2023
## DW = 1.9157, p-value = 0.3432
## alternative hypothesis: true autocorrelation is greater than 0
leveneTest(anova_NOx_2023)
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 11 1.0644 0.4183
## 32
En este caso también se validan las hipótesis iniciales del anova. Como el test F ha resultado significativo, rechazamos \(H_0\) y aceptamos que hay diferencias significativas entre al menos una de las estaciones. Para ver dónde están estas diferencias, vamos a realizar un test de comparación de medias. Concretamente, vamos a usar el test de Tukey, que es moderadamente conservador y tiene un buen control del error tipo I.
Tukey_NOx <- TukeyHSD(anova_NOx_2023, "Origen", conf.level = 0.95)
Tukey_NOx
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = NOx_log ~ Origen, data = df_NOs_2023)
##
## $Origen
## diff
## Quart de Poblet-Paterna - CEAM -0.31420268
## València - Av. França-Paterna - CEAM 0.23686496
## València - Bulevard Sud-Paterna - CEAM 0.74800654
## València - Centre-Paterna - CEAM 1.11695609
## València - Molí del Sol-Paterna - CEAM 0.10615124
## València - Pista de Silla-Paterna - CEAM 0.54815564
## València - Politècnic-Paterna - CEAM -0.67153269
## València - Vivers-Paterna - CEAM -0.56107450
## València Olivereta-Paterna - CEAM 1.59703561
## València Port llit antic Túria-Paterna - CEAM -0.13346297
## València Port Moll Trans. Ponent-Paterna - CEAM -0.29551334
## València - Av. França-Quart de Poblet 0.55106764
## València - Bulevard Sud-Quart de Poblet 1.06220922
## València - Centre-Quart de Poblet 1.43115877
## València - Molí del Sol-Quart de Poblet 0.42035392
## València - Pista de Silla-Quart de Poblet 0.86235832
## València - Politècnic-Quart de Poblet -0.35733001
## València - Vivers-Quart de Poblet -0.24687182
## València Olivereta-Quart de Poblet 1.91123829
## València Port llit antic Túria-Quart de Poblet 0.18073971
## València Port Moll Trans. Ponent-Quart de Poblet 0.01868934
## València - Bulevard Sud-València - Av. França 0.51114159
## València - Centre-València - Av. França 0.88009113
## València - Molí del Sol-València - Av. França -0.13071371
## València - Pista de Silla-València - Av. França 0.31129069
## València - Politècnic-València - Av. França -0.90839765
## València - Vivers-València - Av. França -0.79793946
## València Olivereta-València - Av. França 1.36017065
## València Port llit antic Túria-València - Av. França -0.37032793
## València Port Moll Trans. Ponent-València - Av. França -0.53237830
## València - Centre-València - Bulevard Sud 0.36894955
## València - Molí del Sol-València - Bulevard Sud -0.64185530
## València - Pista de Silla-València - Bulevard Sud -0.19985090
## València - Politècnic-València - Bulevard Sud -1.41953923
## València - Vivers-València - Bulevard Sud -1.30908104
## València Olivereta-València - Bulevard Sud 0.84902907
## València Port llit antic Túria-València - Bulevard Sud -0.88146952
## València Port Moll Trans. Ponent-València - Bulevard Sud -1.04351988
## València - Molí del Sol-València - Centre -1.01080484
## València - Pista de Silla-València - Centre -0.56880044
## València - Politècnic-València - Centre -1.78848878
## València - Vivers-València - Centre -1.67803059
## València Olivereta-València - Centre 0.48007952
## València Port llit antic Túria-València - Centre -1.25041906
## València Port Moll Trans. Ponent-València - Centre -1.41246943
## València - Pista de Silla-València - Molí del Sol 0.44200440
## València - Politècnic-València - Molí del Sol -0.77768393
## València - Vivers-València - Molí del Sol -0.66722575
## València Olivereta-València - Molí del Sol 1.49088436
## València Port llit antic Túria-València - Molí del Sol -0.23961422
## València Port Moll Trans. Ponent-València - Molí del Sol -0.40166459
## València - Politècnic-València - Pista de Silla -1.21968833
## València - Vivers-València - Pista de Silla -1.10923015
## València Olivereta-València - Pista de Silla 1.04887997
## València Port llit antic Túria-València - Pista de Silla -0.68161862
## València Port Moll Trans. Ponent-València - Pista de Silla -0.84366898
## València - Vivers-València - Politècnic 0.11045819
## València Olivereta-València - Politècnic 2.26856830
## València Port llit antic Túria-València - Politècnic 0.53806972
## València Port Moll Trans. Ponent-València - Politècnic 0.37601935
## València Olivereta-València - Vivers 2.15811011
## València Port llit antic Túria-València - Vivers 0.42761153
## València Port Moll Trans. Ponent-València - Vivers 0.26556116
## València Port llit antic Túria-València Olivereta -1.73049858
## València Port Moll Trans. Ponent-València Olivereta -1.89254895
## València Port Moll Trans. Ponent-València Port llit antic Túria -0.16205037
## lwr
## Quart de Poblet-Paterna - CEAM -1.94664095
## València - Av. França-Paterna - CEAM -1.17686806
## València - Bulevard Sud-Paterna - CEAM -1.07711493
## València - Centre-Paterna - CEAM -0.26270606
## València - Molí del Sol-Paterna - CEAM -1.52628703
## València - Pista de Silla-Paterna - CEAM -0.97885054
## València - Politècnic-Paterna - CEAM -2.49665416
## València - Vivers-Paterna - CEAM -2.08808068
## València Olivereta-Paterna - CEAM 0.07002943
## València Port llit antic Túria-Paterna - CEAM -1.95858445
## València Port Moll Trans. Ponent-Paterna - CEAM -1.82251952
## València - Av. França-Quart de Poblet -0.86266538
## València - Bulevard Sud-Quart de Poblet -0.76291225
## València - Centre-Quart de Poblet 0.05149662
## València - Molí del Sol-Quart de Poblet -1.21208435
## València - Pista de Silla-Quart de Poblet -0.66464786
## València - Politècnic-Quart de Poblet -2.18245148
## València - Vivers-Quart de Poblet -1.77387800
## València Olivereta-Quart de Poblet 0.38423211
## València Port llit antic Túria-Quart de Poblet -1.64438177
## València Port Moll Trans. Ponent-Quart de Poblet -1.50831684
## València - Bulevard Sud-València - Av. França -1.12129669
## València - Centre-València - Av. França -0.23222806
## València - Molí del Sol-València - Av. França -1.54444673
## València - Pista de Silla-València - Av. França -0.97926508
## València - Politècnic-València - Av. França -2.54083592
## València - Vivers-València - Av. França -2.08849523
## València Olivereta-València - Av. França 0.06961488
## València Port llit antic Túria-València - Av. França -2.00276620
## València Port Moll Trans. Ponent-València - Av. França -1.82293407
## València - Centre-València - Bulevard Sud -1.23407301
## València - Molí del Sol-València - Bulevard Sud -2.46697677
## València - Pista de Silla-València - Bulevard Sud -1.93131316
## València - Politècnic-València - Bulevard Sud -3.41885963
## València - Vivers-València - Bulevard Sud -3.04054330
## València Olivereta-València - Bulevard Sud -0.88243319
## València Port llit antic Túria-València - Bulevard Sud -2.88078992
## València Port Moll Trans. Ponent-València - Bulevard Sud -2.77498214
## València - Molí del Sol-València - Centre -2.39046700
## València - Pista de Silla-València - Centre -1.82194083
## València - Politècnic-València - Centre -3.39151134
## València - Vivers-València - Centre -2.93117097
## València Olivereta-València - Centre -0.77306086
## València Port llit antic Túria-València - Centre -2.85344162
## València Port Moll Trans. Ponent-València - Centre -2.66560981
## València - Pista de Silla-València - Molí del Sol -1.08500178
## València - Politècnic-València - Molí del Sol -2.60280541
## València - Vivers-València - Molí del Sol -2.19423193
## València Olivereta-València - Molí del Sol -0.03612182
## València Port llit antic Túria-València - Molí del Sol -2.06473569
## València Port Moll Trans. Ponent-València - Molí del Sol -1.92867077
## València - Politècnic-València - Pista de Silla -2.95115059
## València - Vivers-València - Pista de Silla -2.52296316
## València Olivereta-València - Pista de Silla -0.36485305
## València Port llit antic Túria-València - Pista de Silla -2.41308088
## València Port Moll Trans. Ponent-València - Pista de Silla -2.25740200
## València - Vivers-València - Politècnic -1.62100407
## València Olivereta-València - Politècnic 0.53710604
## València Port llit antic Túria-València - Politècnic -1.46125069
## València Port Moll Trans. Ponent-València - Politècnic -1.35544291
## València Olivereta-València - Vivers 0.74437710
## València Port llit antic Túria-València - Vivers -1.30385073
## València Port Moll Trans. Ponent-València - Vivers -1.14817185
## València Port llit antic Túria-València Olivereta -3.46196084
## València Port Moll Trans. Ponent-València Olivereta -3.30628196
## València Port Moll Trans. Ponent-València Port llit antic Túria -1.89351263
## upr
## Quart de Poblet-Paterna - CEAM 1.3182355928
## València - Av. França-Paterna - CEAM 1.6505979705
## València - Bulevard Sud-Paterna - CEAM 2.5731280148
## València - Centre-Paterna - CEAM 2.4966182395
## València - Molí del Sol-Paterna - CEAM 1.7385895165
## València - Pista de Silla-Paterna - CEAM 2.0751618235
## València - Politècnic-Paterna - CEAM 1.1535887830
## València - Vivers-Paterna - CEAM 0.9659316780
## València Olivereta-Paterna - CEAM 3.1240417888
## València Port llit antic Túria-Paterna - CEAM 1.6916584994
## València Port Moll Trans. Ponent-Paterna - CEAM 1.2314928392
## València - Av. França-Quart de Poblet 1.9648006506
## València - Bulevard Sud-Quart de Poblet 2.8873306948
## València - Centre-Quart de Poblet 2.8108209195
## València - Molí del Sol-Quart de Poblet 2.0527921966
## València - Pista de Silla-Quart de Poblet 2.3893645035
## València - Politècnic-Quart de Poblet 1.4677914631
## València - Vivers-Quart de Poblet 1.2801343581
## València Olivereta-Quart de Poblet 3.4382444689
## València Port llit antic Túria-Quart de Poblet 2.0058611795
## València Port Moll Trans. Ponent-Quart de Poblet 1.5456955192
## València - Bulevard Sud-València - Av. França 2.1435798579
## València - Centre-València - Av. França 1.9924103188
## València - Molí del Sol-València - Av. França 1.2830193019
## València - Pista de Silla-València - Av. França 1.6018464573
## València - Politècnic-València - Av. França 0.7240406261
## València - Vivers-València - Av. França 0.4926163118
## València Olivereta-València - Av. França 2.6507264226
## València Port llit antic Túria-València - Av. França 1.2621103425
## València Port Moll Trans. Ponent-València - Av. França 0.7581774729
## València - Centre-València - Bulevard Sud 1.9719721076
## València - Molí del Sol-València - Bulevard Sud 1.1832661760
## València - Pista de Silla-València - Bulevard Sud 1.5316113606
## València - Politècnic-València - Bulevard Sud 0.5797811708
## València - Vivers-València - Bulevard Sud 0.4223812152
## València Olivereta-València - Bulevard Sud 2.5804913260
## València Port llit antic Túria-València - Bulevard Sud 1.1178508872
## València Port Moll Trans. Ponent-València - Bulevard Sud 0.6879423763
## València - Molí del Sol-València - Centre 0.3688573080
## València - Pista de Silla-València - Centre 0.6843399362
## València - Politècnic-València - Centre -0.1854662169
## València - Vivers-València - Centre -0.4248902093
## València Olivereta-València - Centre 1.7332199015
## València Port llit antic Túria-València - Centre 0.3526034996
## València Port Moll Trans. Ponent-València - Centre -0.1593290481
## València - Pista de Silla-València - Molí del Sol 1.9690105798
## València - Politècnic-València - Molí del Sol 1.0474375393
## València - Vivers-València - Molí del Sol 0.8597804343
## València Olivereta-València - Molí del Sol 3.0178905451
## València Port llit antic Túria-València - Molí del Sol 1.5855072557
## València Port Moll Trans. Ponent-València - Molí del Sol 1.1253415955
## València - Politècnic-València - Pista de Silla 0.5117739254
## València - Vivers-València - Pista de Silla 0.3045028689
## València Olivereta-València - Pista de Silla 2.4626129798
## València Port llit antic Túria-València - Pista de Silla 1.0498436418
## València Port Moll Trans. Ponent-València - Pista de Silla 0.5700640301
## València - Vivers-València - Politècnic 1.8419204470
## València Olivereta-València - Politècnic 4.0000305578
## València Port llit antic Túria-València - Politècnic 2.5373901189
## València Port Moll Trans. Ponent-València - Politècnic 2.1074816081
## València Olivereta-València - Vivers 3.5718431252
## València Port llit antic Túria-València - Vivers 2.1590737872
## València Port Moll Trans. Ponent-València - Vivers 1.6792941755
## València Port llit antic Túria-València Olivereta 0.0009636764
## València Port Moll Trans. Ponent-València Olivereta -0.4788159353
## València Port Moll Trans. Ponent-València Port llit antic Túria 1.5694118917
## p adj
## Quart de Poblet-Paterna - CEAM 0.9999061
## València - Av. França-Paterna - CEAM 0.9999763
## València - Bulevard Sud-Paterna - CEAM 0.9453917
## València - Centre-Paterna - CEAM 0.2075893
## València - Molí del Sol-Paterna - CEAM 1.0000000
## València - Pista de Silla-Paterna - CEAM 0.9782032
## València - Politècnic-Paterna - CEAM 0.9739259
## València - Vivers-Paterna - CEAM 0.9741819
## València Olivereta-Paterna - CEAM 0.0340165
## València Port llit antic Túria-Paterna - CEAM 1.0000000
## València Port Moll Trans. Ponent-Paterna - CEAM 0.9999009
## València - Av. França-Quart de Poblet 0.9609161
## València - Bulevard Sud-Quart de Poblet 0.6597771
## València - Centre-Quart de Poblet 0.0365810
## València - Molí del Sol-Quart de Poblet 0.9985620
## València - Pista de Silla-Quart de Poblet 0.6980569
## València - Politècnic-Quart de Poblet 0.9998891
## València - Vivers-Quart de Poblet 0.9999834
## València Olivereta-Quart de Poblet 0.0052928
## València Port llit antic Túria-Quart de Poblet 0.9999999
## València Port Moll Trans. Ponent-Quart de Poblet 1.0000000
## València - Bulevard Sud-València - Av. França 0.9924058
## València - Centre-València - Av. França 0.2337404
## València - Molí del Sol-València - Av. França 1.0000000
## València - Pista de Silla-València - Av. França 0.9992029
## València - Politècnic-València - Av. França 0.7159784
## València - Vivers-València - Av. França 0.5770051
## València Olivereta-València - Av. França 0.0317416
## València Port llit antic Túria-València - Av. França 0.9995477
## València Port Moll Trans. Ponent-València - Av. França 0.9430354
## València - Centre-València - Bulevard Sud 0.9994826
## València - Molí del Sol-València - Bulevard Sud 0.9812744
## València - Pista de Silla-València - Bulevard Sud 0.9999995
## València - Politècnic-València - Bulevard Sud 0.3760656
## València - Vivers-València - Bulevard Sud 0.2901361
## València Olivereta-València - Bulevard Sud 0.8434756
## València Port llit antic Túria-València - Bulevard Sud 0.9137826
## València Port Moll Trans. Ponent-València - Bulevard Sud 0.6128047
## València - Molí del Sol-València - Centre 0.3321907
## València - Pista de Silla-València - Centre 0.8976563
## València - Politècnic-València - Centre 0.0184819
## València - Vivers-València - Centre 0.0022940
## València Olivereta-València - Centre 0.9653129
## València Port llit antic Túria-València - Centre 0.2507750
## València Port Moll Trans. Ponent-València - Centre 0.0166836
## València - Pista de Silla-València - Molí del Sol 0.9960302
## València - Politècnic-València - Molí del Sol 0.9300246
## València - Vivers-València - Molí del Sol 0.9183194
## València Olivereta-València - Molí del Sol 0.0606568
## València Port llit antic Túria-València - Molí del Sol 0.9999980
## València Port Moll Trans. Ponent-València - Molí del Sol 0.9982632
## València - Politècnic-València - Pista de Silla 0.3873387
## València - Vivers-València - Pista de Silla 0.2437084
## València Olivereta-València - Pista de Silla 0.3151080
## València Port llit antic Túria-València - Pista de Silla 0.9581993
## València Port Moll Trans. Ponent-València - Pista de Silla 0.6263147
## València - Vivers-València - Politècnic 1.0000000
## València Olivereta-València - Politècnic 0.0030294
## València Port llit antic Túria-València - Politècnic 0.9978766
## València Port Moll Trans. Ponent-València - Politècnic 0.9996999
## València Olivereta-València - Vivers 0.0003640
## València Port llit antic Túria-València - Vivers 0.9990120
## València Port Moll Trans. Ponent-València - Vivers 0.9999260
## València Port llit antic Túria-València Olivereta 0.0502301
## València Port Moll Trans. Ponent-València Olivereta 0.0023023
## València Port Moll Trans. Ponent-València Port llit antic Túria 0.9999999
Las estaciones entre las que hay una diferencia significativa de la concentración de \(NO_x\) son las siguientes:
hist((df_NOs$O3), breaks = 100, main = "Distribución de O3", xlab = "O3", ylab = "Frecuencia")
La variable \(O_3\) tiene una distribución peculiar, porque parece tiene una parte con una distribución normal y parece haber una segunda población a la izquierda que se juntan y se convierten en una distribución bimodal.
Sabemos que el grado de formación del ozono se ven muy incrementados con el au- mento de la radiación solar, las emisiones antropogénicas de precursores y el ciclo biológico de emisiones biogénicas de COVs. También sabemos que sus niveles son superiores en la periferia de las ciudades y en zonas rurales, y que los fines de semana aumenta su presencia.
A partir de esta información vamos a ver la distribución del \(O_3\) en función de la estación para confirmar la presencia de dos poblaciones.
df_NOs %>%
filter(!is.na(O3)) %>%
ggplot(aes(x = O3)) +
geom_histogram(bins = 100,
fill = "#6EC6FF",
color = "red",
alpha = 0.7) +
facet_wrap(~ Origen, scales = "free_y") +
labs(
title = "Distribución de O3 por estación",
x = "O3 (µg/m³)",
y = "Frecuencia"
) +
theme_minimal() +
theme(
strip.text = element_text(face = "bold"),
axis.title = element_text(face = "bold")
)
La distribución de \(O_3\) en función de la estación es muy parecida a la distribución general. Las distribuciones que cambian son:
Aún así, con esta información no tenemos pruebas suficientes como para sacar conclusiones definitivas sobre la presencia de dos poblaciones. Vamos a ver cómo se distribuye el O3 a lo largo del tiempo para profundizar más.
# Ver distribución general para la variable O3 en el tiempo
ggplot(df_NOs, aes(x = FechaHora)) +
geom_line(aes(y = O3, color = "O3")) +
labs(title = "O3 a lo largo del tiempo",
x = "Fecha y Hora",
y = "Concentración (µg/m³)",
color = "Contaminante") +
theme_minimal()
## Warning: Removed 23961 rows containing missing values or values outside the scale range
## (`geom_line()`).
Viendo el comportamiento del \(O_3\) a lo largo del tiempo tampoco encontramos una explicación a la distribución bimodal. Sin embargo, sí que vemos que el ozono tiene un comportamiento estacional, con picos en verano y mínimos en invierno. Esto es debido a la formación de ozono troposférico, que se produce por la reacción de los óxidos de nitrógeno y los compuestos orgánicos volátiles en presencia de luz solar. Al realizar el cruce con los factores meteorológicos comprobaremos esta hipótesis.
Vamos a ver la distribución del ozono dependiendo de si es invierno o verano.
df_NOs_verano <- df_NOs %>%
filter(month(FechaHora) %in% 4:9)
meses_restantes = setdiff(1:12, 4:9)
df_NOs_invierno <- df_NOs %>%
filter(month(FechaHora) %in% meses_restantes)
unique(month(df_NOs_invierno$FechaHora))
## [1] 12 1 2 3 10 11
hist((df_NOs_verano$O3), breaks = 100, main = "Distribución de O3", xlab = "O3", ylab = "Frecuencia")
hist((df_NOs_invierno$O3), breaks = 100, main = "Distribución de O3", xlab = "O3", ylab = "Frecuencia")
Ahora sí que vemos que las dos poblaciones se corresponden a los meses de verano y a los de invierno. En verano la distribución es normal, mientras que en invierno la distribución es bimodal. La teoría es que el ozono se forma por la reacción de los óxidos de nitrógeno y los compuestos orgánicos volátiles en presencia de luz solar, y en invierno la radiación solar es menor, lo que provoca que la formación de ozono sea menor. Más adelante intentaremos validar esta hipótesis cruzando el ozono con los factores meteorológicos y con otros contaminantes.
Como aún no podemos comprender el comportamiento del ozono en la época de invierno, vamos a centrarnos en la época de verano. En este caso, la distribución es normal y no tiene valores atípicos.
Este comportamiento ya lo hemos estudiado antes para darle una explicación a la distribución bimodal. Así, vamos a pasar directamente a estudiar el tiempo granularmente
df_O3_anual <- df_NOs %>%
group_by(year = year(FechaHora)) %>%
summarise(across(c("O3"), mean, na.rm = TRUE)) %>%
filter(!is.na(year)) %>%
ungroup()
ggplot(df_O3_anual, aes(x = year, y = O3)) +
geom_area(fill = "#6EC6FF", alpha = 0.4) +
geom_line(color = "#4682B4", size = 1.2) +
geom_point(color = "#4682B4", size = 3) +
scale_x_continuous(breaks = 2019:2024, limits = c(2019, 2024)) +
labs(
title = "Evolución anual de la concentración media de O3",
subtitle = "Valores medios de todas las estaciones en Valencia",
x = "Año",
y = "Concentración media de O3 (µg/m³)",
caption = "Source: your air pollution data"
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
Vemos como apenas hay cambios en la concentración de ozono a lo largo de los años, a excepción de 2020 que ya sabemos que fue un año atípico por el COVID-19. Aún así, se ve una ligera tendencia decreciente en la concentración de ozono a lo largo de los años. Vamos a comprobar estadísticamente si esta tendencia es significativa o no mediante un anova.
anova_O3 <- aov(O3 ~ year, data = df_O3_anual)
summary(anova_O3)
## Df Sum Sq Mean Sq F value Pr(>F)
## year 1 0.095 0.0948 0.036 0.86
## Residuals 4 10.654 2.6635
par(mfrow = c(1, 1))
plot(anova_O3)
dw <- dwtest(anova_O3)
print(dw)
##
## Durbin-Watson test
##
## data: anova_O3
## DW = 2.873, p-value = 0.7432
## alternative hypothesis: true autocorrelation is greater than 0
Vemos que se cumplen las hipótesis inciales del anova, y que además el test F no significativo. Por lo tanto, aceptamos \(H_0\) y no hay diferencias significativas entre los años. Esto es consistente con lo que hemos visto en el gráfico de la evolución anual de la concentración media de ozono.
df_O3_mensual <- df_NOs %>%
group_by(month = month(FechaHora, label = TRUE)) %>%
summarise(across(c("O3"), mean, na.rm = TRUE)) %>%
filter(!is.na(month)) %>%
ungroup()
ggplot(df_O3_mensual, aes(x = month, y = O3, group = 1)) +
geom_area(fill = "#6EC6FF", alpha = 0.4) +
geom_line(color = "#4682B4", size = 1.2) +
geom_point(color = "#4682B4", size = 3) +
labs(
title = "Evolución media mensual de la concentración de O3",
subtitle = "Media agregada sobre todos los años",
x = "Mes",
y = "Concentración media de O3 (µg/m³)",
) +
theme_minimal(base_size = 14) +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f7f7f7", color = NA),
panel.grid.major = element_line(color = "gray80"),
panel.grid.minor = element_blank(),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "gray20"),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "gray40", hjust = 0.5),
plot.caption = element_text(size = 8, color = "gray60")
)
La concentración de O3 tiene su pico en los meses de abril, mayo y junio, y a partir de estos los valores, la media mensual va descendiendo hasta alcanzar los mínimos en los meses de enero y diciembre. Esto confirma la hipótesis de que hay dos poblaciones de ozono, una en verano y otra en invierno.
df_O3_semanal <- df_NOs %>%
# crear weekday y eliminar filas con FechaHora NA
mutate(
weekday = wday(FechaHora,
label = TRUE,
abbr = TRUE,
week_start = 1)
) %>%
filter(!is.na(weekday)) %>% # quitar el nivel que corresponde a NA
group_by(weekday) %>%
summarise(
O3 = mean(O3, na.rm = TRUE),
.groups = "drop"
)
ggplot(df_O3_semanal, aes(x = weekday, y = O3, group = 1)) +
geom_area(fill = "#6EC6FF", alpha = 0.4) +
geom_line(color = "#4682B4", size = 1.2) +
geom_point(color = "#4682B4", size = 3) +
labs(
title = "Concentración media de O3 por día de la semana",
x = "Día de la semana",
y = "Concentración media de O3 (µg/m³)"
) +
theme_minimal(base_size = 14)
Como se dice en la información estudiada, los fines de semana la concentración de ozono es mucho más baja que entre semana. Además, entre semana se mantienen constantes los valores.
df_O3_horario <- df_NOs %>%
# crear weekday y eliminar filas con FechaHora NA
mutate(
hour = hour(FechaHora)
) %>%
filter(!is.na(hour)) %>% # quitar el nivel que corresponde a NA
group_by(hour) %>%
summarise(
O3 = mean(O3, na.rm = TRUE),
.groups = "drop"
)
ggplot(df_O3_horario, aes(x = hour, y = O3, group = 1)) +
geom_area(fill = "#6EC6FF", alpha = 0.4) +
geom_line(color = "#4682B4", size = 1.2) +
geom_point(color = "#4682B4", size = 3) +
labs(
title = "Concentración media de O3 por hora del día",
x = "Hora del día",
y = "Concentración media de O3 (µg/m³)"
) +
theme_minimal(base_size = 14)
En el comportamiento del \(O_3\) por horas vemos como hay una clara tendencia: la concentración de \(O_3\) tiende a aumentar cuando aumenta la radiación solar, y a disminuir cuando disminuye. Esto es consistente con lo que hemos visto en la distribución del ozono a lo largo del tiempo y con la teoría de que el ozono se forma por la reacción de los óxidos de nitrógeno y los compuestos orgánicos volátiles en presencia de luz solar.
Antes de pasar a estudiar el comportamiento por zonas, vamos a ver cómo se comportan los valores faltantes de estos contaminantes. Entendiendo esto, podríamos hacer una imputación correcta y precisa de los faltantes para potenciar posteriormente el análisis.
En primer lugar, vamos a ver el porcentaje de faltantes que tenemos para \(O_3\).
df_NOs %>% summarise(across(c("O3"), ~ sum(is.na(.)) / n() * 100))
## O3
## 1 15.75828
El porcentaje de faltantes no es demasiado alto. Vamos a profundizar un poco más en el análisis de los valores faltantes viendo como se distribuyen por zonas.
missing_values_zona_O3 <- df_NOs %>%
group_by(Origen) %>%
summarise(
Porcentaje_Faltantes = sum(is.na(O3)) / n() * 100,
.groups = "drop"
) %>%
arrange(Porcentaje_Faltantes, Origen)
missing_values_zona_O3
## # A tibble: 18 × 2
## Origen Porcentaje_Faltantes
## <fct> <dbl>
## 1 Massanassa_UM 0.258
## 2 València - Av. França 0.720
## 3 València - Molí del Sol 0.919
## 4 Sedavi UM 0.932
## 5 València - Vivers 0.986
## 6 Quart de Poblet 1.12
## 7 València - Politècnic 1.58
## 8 Torrent-El Vedat 2.05
## 9 Paterna - CEAM 2.35
## 10 València Port llit antic Túria 2.69
## 11 València - Bulevard Sud 3.47
## 12 València - Pista de Silla 3.67
## 13 València Port Moll Trans. Ponent 5.11
## 14 Benetusser UM 100
## 15 Catarroja UM 100
## 16 Paiporta UM 100
## 17 València - Centre 100
## 18 València Olivereta 100
Vemos que en general hay muy pocos faltantes para todas las estaciones (< 5%). Sin embargo las estaciones de Benetusser UM, Catarroja UM, Paiporta UM, València - Centre y València Olivereta no registran ningún dato de ozono. Por lo tanto, para el análisis de estos contaminantes no vamos a considerar estas estaciones.
df_O3 = df_NOs %>%
filter(Origen != "Benetusser UM" & Origen != "Catarroja UM" & Origen != "Paiporta UM" & Origen != "València - Centre" & Origen != "València Olivereta")
Vamos a ver cómo se distribuyen los valores faltantes de estos contaminantes a lo largo del tiempo, con el objetivo de detectar si hay algún patrón en la distribución de los valores faltantes.
df_O3 %>%
mutate(missing = is.na(O3)) %>%
ggplot(aes(x = FechaHora, y = Origen, fill = missing)) +
geom_tile() +
scale_fill_manual(values = c(`TRUE` = "red", `FALSE` = "grey90")) +
labs(title = "Dónde y cuándo faltan valores de O3",
x = "Fecha y hora", y = "Zona",
fill = "Es NA") +
theme_minimal()
## Warning: Removed 20646 rows containing missing values or values outside the scale range
## (`geom_tile()`).
A la vista del gráfico no podemos afirmar la existencia de ningún patrón de faltantes ni entre estaciones ni a lo largo del tiempo. Además vemos que las estaciones de “Sedavi UM” y “Massanassa_UM” tienen una muestra insignificante de datos, por lo que no las vamos a considerar para el análisis posterior.
df_O3 = df_O3 %>%
filter(Origen != "Sedavi UM" & Origen != "Massanassa_UM")
Vamos a intentar entender el comportamiento de estas variables en las distintas zonas donde se miden.
Para ello vamos a realizar un ANOVA intentando ver si hay alguna diferencia significativa entre zonas.
Como hemos visto en la distribución, vamos a coger un día de verano y eliminar la dimensión tiempo. Además, no hace falta en principio hacerle ninguna transformación a la variable porque la distribución es normal y no tiene valores atípicos. Por lo tanto, vamos a realizar el anova directamente con la variable \(O_3\) y eliminando la dimensión del tiempo.
df_O3_2023 = df_O3 %>%
filter(FechaHora >= "2023-04-01" & FechaHora < "2023-10-01") %>%
filter(hour(FechaHora) == 12) %>%
filter(wday(FechaHora) == 2) %>%
sample_n(20)
anova_O3_2023 <- aov(O3 ~ Origen, data = df_O3_2023)
summary(anova_O3_2023)
## Df Sum Sq Mean Sq F value Pr(>F)
## Origen 8 842 105.2 0.205 0.983
## Residuals 11 5645 513.1
par(mfrow = c(1, 1))
plot(anova_O3_2023)
## Warning: not plotting observations with leverage one:
## 9, 13
dw <- dwtest(anova_O3_2023)
print(dw)
##
## Durbin-Watson test
##
## data: anova_O3_2023
## DW = 2.4731, p-value = 0.8538
## alternative hypothesis: true autocorrelation is greater than 0
leveneTest(anova_O3_2023)
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 8 0.7162 0.6752
## 11
Como se han validado las tres hipótesis del anova y además el test F ha resultado no significativo, aceptamos \(H_0\) y no hay diferencias significativas entre las estaciones. Esto es consistente con lo que hemos visto en la distribución de los valores de ozono a lo largo del tiempo y por estaciones. Por lo tanto, no vamos a realizar el test de Tukey.
Las partículas PM10 y PM2.5 muestran un comportamiento prácticamente idéntico: se registran en las mismas estaciones, sus distribuciones son muy similares, y presentan porcentajes de datos ausentes casi idénticos, tanto en cantidad como en su distribución temporal. Las medias anuales, mensuales, diarias y horarias siguen patrones prácticamente coincidentes. Esta fuerte similitud, unida a sus fuentes de emisión compartidas —tanto naturales como antropogénicas—, apunta a una alta correlación entre ambas.
En cuanto al SO₂, su distribución presenta un perfil unimodal más estable que el de las partículas. Sin embargo, sus medias en distintas escalas temporales evolucionan de forma paralela a PM10 y PM2.5, aunque con menor intensidad, lo que indica algunos orígenes comunes de emisión.
El CO, por su parte, comparte con SO₂ una distribución unimodal y una evolución temporal semejante, reflejo de fuentes similares. No obstante, destaca por tener una proporción de datos faltantes considerablemente mayor, duplicando la de PM10 y PM2.5 y triplicando la de SO₂.
Todos los contaminantes analizados presentan variaciones significativas según la estación de medición, lo que resalta la influencia del entorno geográfico. Asimismo, se han identificado coincidencias en fechas con valores ausentes en distintos años, lo que sugiere ciertos patrones de ausencia que podrían no ser aleatorios.
Desde el punto de vista de análisis multivariante, se espera una relación intensa entre PM10 y PM2.5, y en menor medida con SO₂ y CO.
Por otro lado, los contaminantes NO, NO₂ y NOₓ presentan comportamientos muy similares, lo cual es coherente desde el punto de vista químico: NO₂ es producto de la oxidación del NO, y NOₓ representa su suma. Estos tres muestran diferencias relevantes entre zonas, así como variaciones sistemáticas a lo largo del año, el mes, el día de la semana y la franja horaria.
En contraste, el O₃ (ozono) se comporta de forma diferenciada, con un marcado componente estacional: alcanza sus valores máximos en verano y mínimos en invierno. También exhibe variabilidad geográfica significativa, diferenciándose claramente del resto de contaminantes tanto en su evolución como en su origen.
Respecto a los datos ausentes, no se ha detectado un patrón sistemático en todos los contaminantes, lo que sugiere que su aparición es mayoritariamente aleatoria. Para los análisis posteriores, se anticipa una correlación sólida entre PM10 y PM2.5, otra notable entre NO, NO₂ y NOₓ, una conexión más débil con SO₂ y CO, y un comportamiento relativamente independiente del O₃. Además, muchas de estas variables parecen estar condicionadas por factores externos como la meteorología (temperatura y estacionalidad) y el tráfico, dado que sus concentraciones fluctúan según el momento del día y el día de la semana.